From cae1c11414912bf77a62aebd65ced321f0b9da51 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 27 Oct 2008 15:22:46 +0000 Subject: uwb: reference count reservations Reference counting the struct uwb_rsv's is safer and easier to get right than the transferring ownership of the structures from the PAL to reservation manager. This fixes an oops in the debug PAL after a reservation timed out. Signed-off-by: David Vrabel --- include/linux/uwb.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/uwb.h b/include/linux/uwb.h index f9ccbd9a2ced..010ee708304d 100644 --- a/include/linux/uwb.h +++ b/include/linux/uwb.h @@ -201,6 +201,7 @@ struct uwb_rsv { struct uwb_rc *rc; struct list_head rc_node; struct list_head pal_node; + struct kref kref; struct uwb_dev *owner; struct uwb_rsv_target target; -- cgit v1.2.3 From 4d2bea4ca0adb4cebfbf89d34869c74081c42577 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 27 Oct 2008 15:42:31 +0000 Subject: wusb: do a proper channel stop When stopping the WUSB channel the host should send Channel Stop IEs giving the WUSB Channel Time of the last MMC. Both WHCI and HWA hosts provide a channel stop command for this. Signed-off-by: David Vrabel --- drivers/usb/host/hwa-hc.c | 102 +++++++++++++++++++--------------------- drivers/usb/host/whci/whcd.h | 2 +- drivers/usb/host/whci/whci-hc.h | 2 + drivers/usb/host/whci/wusb.c | 15 ++++-- drivers/usb/wusbcore/mmc.c | 8 +--- drivers/usb/wusbcore/wusbhc.h | 24 ++++++---- include/linux/usb/wusb-wa.h | 1 + 7 files changed, 80 insertions(+), 74 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 64be4d88df11..0e18989e1658 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -171,11 +171,6 @@ static int hwahc_op_start(struct usb_hcd *usb_hcd) if (result < 0) goto error_set_cluster_id; - result = wa_nep_arm(&hwahc->wa, GFP_KERNEL); - if (result < 0) { - dev_err(dev, "cannot listen to notifications: %d\n", result); - goto error_stop; - } usb_hcd->uses_new_polling = 1; usb_hcd->poll_rh = 1; usb_hcd->state = HC_STATE_RUNNING; @@ -185,8 +180,6 @@ out: d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result); return result; -error_stop: - __wa_stop(&hwahc->wa); error_set_cluster_id: wusb_cluster_id_put(wusbhc->cluster_id); error_cluster_id_get: @@ -194,39 +187,6 @@ error_cluster_id_get: } -/* - * FIXME: break this function up - */ -static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc) -{ - int result; - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct device *dev = &hwahc->wa.usb_iface->dev; - - /* Set up a Host Info WUSB Information Element */ - d_fnstart(4, dev, "(hwahc %p)\n", hwahc); - result = -ENOSPC; - - result = __wa_set_feature(&hwahc->wa, WA_ENABLE); - if (result < 0) { - dev_err(dev, "error commanding HC to start: %d\n", result); - goto error_stop; - } - result = __wa_wait_status(&hwahc->wa, WA_ENABLE, WA_ENABLE); - if (result < 0) { - dev_err(dev, "error waiting for HC to start: %d\n", result); - goto error_stop; - } - result = 0; -out: - d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result); - return result; - -error_stop: - result = __wa_clear_feature(&hwahc->wa, WA_ENABLE); - goto out; -} - static int hwahc_op_suspend(struct usb_hcd *usb_hcd, pm_message_t msg) { struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); @@ -246,18 +206,6 @@ static int hwahc_op_resume(struct usb_hcd *usb_hcd) return -ENOSYS; } -static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc) -{ - int result; - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct device *dev = &hwahc->wa.usb_iface->dev; - - d_fnstart(4, dev, "(hwahc %p)\n", hwahc); - /* Nothing for now */ - d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result); - return; -} - /* * No need to abort pipes, as when this is called, all the children * has been disconnected and that has done it [through @@ -275,8 +223,6 @@ static void hwahc_op_stop(struct usb_hcd *usb_hcd) d_fnstart(4, dev, "(hwahc %p)\n", hwahc); mutex_lock(&wusbhc->mutex); wusbhc_stop(wusbhc); - wa_nep_disarm(&hwahc->wa); - result = __wa_stop(&hwahc->wa); wusb_cluster_id_put(wusbhc->cluster_id); mutex_unlock(&wusbhc->mutex); d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result); @@ -325,6 +271,54 @@ static void hwahc_op_endpoint_disable(struct usb_hcd *usb_hcd, rpipe_ep_disable(&hwahc->wa, ep); } +static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc) +{ + int result; + struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); + struct device *dev = &hwahc->wa.usb_iface->dev; + + result = __wa_set_feature(&hwahc->wa, WA_ENABLE); + if (result < 0) { + dev_err(dev, "error commanding HC to start: %d\n", result); + goto error_stop; + } + result = __wa_wait_status(&hwahc->wa, WA_ENABLE, WA_ENABLE); + if (result < 0) { + dev_err(dev, "error waiting for HC to start: %d\n", result); + goto error_stop; + } + result = wa_nep_arm(&hwahc->wa, GFP_KERNEL); + if (result < 0) { + dev_err(dev, "cannot listen to notifications: %d\n", result); + goto error_stop; + } + return result; + +error_stop: + __wa_clear_feature(&hwahc->wa, WA_ENABLE); + return result; +} + +static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc, int delay) +{ + struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); + struct wahc *wa = &hwahc->wa; + u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; + int ret; + + ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), + WUSB_REQ_CHAN_STOP, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + delay * 1000, + iface_no, + NULL, 0, 1000 /* FIXME: arbitrary */); + if (ret == 0) + msleep(delay); + + wa_nep_disarm(&hwahc->wa); + __wa_stop(&hwahc->wa); +} + /* * Set the UWB MAS allocation for the WUSB cluster * diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h index 1d2a53bd39fd..1bbb8cb6bf80 100644 --- a/drivers/usb/host/whci/whcd.h +++ b/drivers/usb/host/whci/whcd.h @@ -136,7 +136,7 @@ int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len); /* wusb.c */ int whc_wusbhc_start(struct wusbhc *wusbhc); -void whc_wusbhc_stop(struct wusbhc *wusbhc); +void whc_wusbhc_stop(struct wusbhc *wusbhc, int delay); int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, u8 handle, struct wuie_hdr *wuie); int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle); diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h index bff1eb7a35cf..51df7e313b38 100644 --- a/drivers/usb/host/whci/whci-hc.h +++ b/drivers/usb/host/whci/whci-hc.h @@ -410,6 +410,8 @@ struct dn_buf_entry { # define WUSBDNTSCTRL_SLOTS(s) ((s) << 0) #define WUSBTIME 0x68 +# define WUSBTIME_CHANNEL_TIME_MASK 0x00ffffff + #define WUSBBPST 0x6c #define WUSBDIBUPDATED 0x70 diff --git a/drivers/usb/host/whci/wusb.c b/drivers/usb/host/whci/wusb.c index 66e4ddcd961d..2befd475def4 100644 --- a/drivers/usb/host/whci/wusb.c +++ b/drivers/usb/host/whci/wusb.c @@ -64,8 +64,9 @@ static int whc_update_di(struct whc *whc, int idx) } /* - * WHCI starts and stops MMCs based on there being a valid GTK so - * these need only start/stop the asynchronous and periodic schedules. + * WHCI starts MMCs based on there being a valid GTK so these need + * only start/stop the asynchronous and periodic schedules and send a + * channel stop command. */ int whc_wusbhc_start(struct wusbhc *wusbhc) @@ -78,12 +79,20 @@ int whc_wusbhc_start(struct wusbhc *wusbhc) return 0; } -void whc_wusbhc_stop(struct wusbhc *wusbhc) +void whc_wusbhc_stop(struct wusbhc *wusbhc, int delay) { struct whc *whc = wusbhc_to_whc(wusbhc); + u32 stop_time, now_time; + int ret; pzl_stop(whc); asl_stop(whc); + + now_time = le_readl(whc->base + WUSBTIME) & WUSBTIME_CHANNEL_TIME_MASK; + stop_time = (now_time + ((delay * 8) << 7)) & 0x00ffffff; + ret = whc_do_gencmd(whc, WUSBGENCMDSTS_CHAN_STOP, stop_time, NULL, 0); + if (ret == 0) + msleep(delay); } int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/usb/wusbcore/mmc.c index cfa77a01cebd..af2aee0fdffa 100644 --- a/drivers/usb/wusbcore/mmc.c +++ b/drivers/usb/wusbcore/mmc.c @@ -250,18 +250,14 @@ error_alloc: * wusbhc_stop - stop transmitting MMCs * @wusbhc: the HC to stop * - * Send a Host Disconnect IE, wait, remove all the MMCs (stop sending MMCs). - * - * If we can't allocate a Host Stop IE, screw it, we don't notify the - * devices we are disconnecting... + * Stops the WUSB channel and removes the cluster reservation. */ void wusbhc_stop(struct wusbhc *wusbhc) { if (wusbhc->active) { wusbhc->active = 0; - wusbhc->stop(wusbhc); + wusbhc->stop(wusbhc, WUSB_CHANNEL_STOP_DELAY_MS); wusbhc_sec_stop(wusbhc); - __wusbhc_host_disconnect_ie(wusbhc); wusbhc_devconnect_stop(wusbhc); wusbhc_rsv_terminate(wusbhc); } diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h index d0c132434f1b..b9bdf5a5f11b 100644 --- a/drivers/usb/wusbcore/wusbhc.h +++ b/drivers/usb/wusbcore/wusbhc.h @@ -64,6 +64,13 @@ #include #include +/* + * Time from a WUSB channel stop request to the last transmitted MMC. + * + * This needs to be > 4.096 ms in case no MMCs can be transmitted in + * zone 0. + */ +#define WUSB_CHANNEL_STOP_DELAY_MS 8 /** * Wireless USB device @@ -198,21 +205,18 @@ struct wusb_port { * @mmcies_max Max number of Information Elements this HC can send * in its MMC. Read-only. * + * @start Start the WUSB channel. + * + * @stop Stop the WUSB channel after the specified number of + * milliseconds. Channel Stop IEs should be transmitted + * as required by [WUSB] 4.16.2.1. + * * @mmcie_add HC specific operation (WHCI or HWA) for adding an * MMCIE. * * @mmcie_rm HC specific operation (WHCI or HWA) for removing an * MMCIE. * - * @enc_types Array which describes the encryptions methods - * supported by the host as described in WUSB1.0 -- - * one entry per supported method. As of WUSB1.0 there - * is only four methods, we make space for eight just in - * case they decide to add some more (and pray they do - * it in sequential order). if 'enc_types[enc_method] - * != 0', then it is supported by the host. enc_method - * is USB_ENC_TYPE*. - * * @set_ptk: Set the PTK and enable encryption for a device. Or, if * the supplied key is NULL, disable encryption for that * device. @@ -269,7 +273,7 @@ struct wusbhc { u8 mmcies_max; /* FIXME: make wusbhc_ops? */ int (*start)(struct wusbhc *wusbhc); - void (*stop)(struct wusbhc *wusbhc); + void (*stop)(struct wusbhc *wusbhc, int delay); int (*mmcie_add)(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, u8 handle, struct wuie_hdr *wuie); int (*mmcie_rm)(struct wusbhc *wusbhc, u8 handle); diff --git a/include/linux/usb/wusb-wa.h b/include/linux/usb/wusb-wa.h index a102561e7026..fb7c359bdfba 100644 --- a/include/linux/usb/wusb-wa.h +++ b/include/linux/usb/wusb-wa.h @@ -51,6 +51,7 @@ enum { WUSB_REQ_GET_TIME = 25, WUSB_REQ_SET_STREAM_IDX = 26, WUSB_REQ_SET_WUSB_MAS = 27, + WUSB_REQ_CHAN_STOP = 28, }; -- cgit v1.2.3 From 1cde7f68ced8d10a20dd2370e9d1d22ab3c1ea5c Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 27 Oct 2008 16:48:09 +0000 Subject: uwb: order IEs by element ID ECMA-368 requires that IEs in a beacon must be sorted by element ID. Most hardware uses the ordering in the Set IE URC command so get the ordering right on the host. Also refactor the IE management code: - use uwb_ie_next() instead of uwb_ie_for_each(). - remove unnecessary functions. - API is now only uwb_rc_ie_add() and uwb_rc_ie_rm(). Signed-off-by: David Vrabel --- drivers/uwb/beacon.c | 24 +-- drivers/uwb/ie.c | 463 +++++++++++++---------------------------- drivers/uwb/lc-rc.c | 25 --- drivers/uwb/uwb-internal.h | 16 +- drivers/uwb/wlp/wlp-internal.h | 4 - include/linux/uwb.h | 18 +- 6 files changed, 172 insertions(+), 378 deletions(-) (limited to 'include/linux') diff --git a/drivers/uwb/beacon.c b/drivers/uwb/beacon.c index 46b18eec5026..ad823987cede 100644 --- a/drivers/uwb/beacon.c +++ b/drivers/uwb/beacon.c @@ -349,22 +349,22 @@ ssize_t uwb_bce_print_IEs(struct uwb_dev *uwb_dev, struct uwb_beca_e *bce, ssize_t result = 0; struct uwb_rc_evt_beacon *be; struct uwb_beacon_frame *bf; - struct uwb_buf_ctx ctx = { - .buf = buf, - .bytes = 0, - .size = size - }; + int ies_len; + struct uwb_ie_hdr *ies; mutex_lock(&bce->mutex); + be = bce->be; - if (be == NULL) - goto out; - bf = (void *) be->BeaconInfo; - uwb_ie_for_each(uwb_dev, uwb_ie_dump_hex, &ctx, - bf->IEData, be->wBeaconInfoLength - sizeof(*bf)); - result = ctx.bytes; -out: + if (be) { + bf = (struct uwb_beacon_frame *)bce->be->BeaconInfo; + ies_len = be->wBeaconInfoLength - sizeof(struct uwb_beacon_frame); + ies = (struct uwb_ie_hdr *)bf->IEData; + + result = uwb_ie_dump_hex(ies, ies_len, buf, size); + } + mutex_unlock(&bce->mutex); + return result; } diff --git a/drivers/uwb/ie.c b/drivers/uwb/ie.c index cf6f3d152b9d..ab976686175b 100644 --- a/drivers/uwb/ie.c +++ b/drivers/uwb/ie.c @@ -25,8 +25,6 @@ */ #include "uwb-internal.h" -#define D_LOCAL 0 -#include /** * uwb_ie_next - get the next IE in a buffer @@ -60,6 +58,42 @@ struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len) } EXPORT_SYMBOL_GPL(uwb_ie_next); +/** + * uwb_ie_dump_hex - print IEs to a character buffer + * @ies: the IEs to print. + * @len: length of all the IEs. + * @buf: the destination buffer. + * @size: size of @buf. + * + * Returns the number of characters written. + */ +int uwb_ie_dump_hex(const struct uwb_ie_hdr *ies, size_t len, + char *buf, size_t size) +{ + void *ptr; + const struct uwb_ie_hdr *ie; + int r = 0; + u8 *d; + + ptr = (void *)ies; + for (;;) { + ie = uwb_ie_next(&ptr, &len); + if (!ie) + break; + + r += scnprintf(buf + r, size - r, "%02x %02x", + (unsigned)ie->element_id, + (unsigned)ie->length); + d = (uint8_t *)ie + sizeof(struct uwb_ie_hdr); + while (d != ptr && r < size) + r += scnprintf(buf + r, size - r, " %02x", (unsigned)*d++); + if (r < size) + buf[r++] = '\n'; + }; + + return r; +} + /** * Get the IEs that a radio controller is sending in its beacon * @@ -70,6 +104,7 @@ EXPORT_SYMBOL_GPL(uwb_ie_next); * anything. Once done with the iedata buffer, call * uwb_rc_ie_release(iedata). Don't call kfree on it. */ +static ssize_t uwb_rc_get_ie(struct uwb_rc *uwb_rc, struct uwb_rc_evt_get_ie **pget_ie) { ssize_t result; @@ -78,148 +113,35 @@ ssize_t uwb_rc_get_ie(struct uwb_rc *uwb_rc, struct uwb_rc_evt_get_ie **pget_ie) struct uwb_rceb *reply = NULL; struct uwb_rc_evt_get_ie *get_ie; - d_fnstart(3, dev, "(%p, %p)\n", uwb_rc, pget_ie); - result = -ENOMEM; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) - goto error_kzalloc; + return -ENOMEM; + cmd->bCommandType = UWB_RC_CET_GENERAL; cmd->wCommand = cpu_to_le16(UWB_RC_CMD_GET_IE); result = uwb_rc_vcmd(uwb_rc, "GET_IE", cmd, sizeof(*cmd), UWB_RC_CET_GENERAL, UWB_RC_CMD_GET_IE, &reply); + kfree(cmd); if (result < 0) - goto error_cmd; + return result; + get_ie = container_of(reply, struct uwb_rc_evt_get_ie, rceb); if (result < sizeof(*get_ie)) { dev_err(dev, "not enough data returned for decoding GET IE " "(%zu bytes received vs %zu needed)\n", result, sizeof(*get_ie)); - result = -EINVAL; + return -EINVAL; } else if (result < sizeof(*get_ie) + le16_to_cpu(get_ie->wIELength)) { dev_err(dev, "not enough data returned for decoding GET IE " "payload (%zu bytes received vs %zu needed)\n", result, sizeof(*get_ie) + le16_to_cpu(get_ie->wIELength)); - result = -EINVAL; - } else - *pget_ie = get_ie; -error_cmd: - kfree(cmd); -error_kzalloc: - d_fnend(3, dev, "(%p, %p) = %d\n", uwb_rc, pget_ie, (int)result); - return result; -} -EXPORT_SYMBOL_GPL(uwb_rc_get_ie); - - -/* - * Given a pointer to an IE, print it in ASCII/hex followed by a new line - * - * @ie_hdr: pointer to the IE header. Length is in there, and it is - * guaranteed that the ie_hdr->length bytes following it are - * safely accesible. - * - * @_data: context data passed from uwb_ie_for_each(), an struct output_ctx - */ -int uwb_ie_dump_hex(struct uwb_dev *uwb_dev, const struct uwb_ie_hdr *ie_hdr, - size_t offset, void *_ctx) -{ - struct uwb_buf_ctx *ctx = _ctx; - const u8 *pl = (void *)(ie_hdr + 1); - u8 pl_itr; - - ctx->bytes += scnprintf(ctx->buf + ctx->bytes, ctx->size - ctx->bytes, - "%02x %02x ", (unsigned) ie_hdr->element_id, - (unsigned) ie_hdr->length); - pl_itr = 0; - while (pl_itr < ie_hdr->length && ctx->bytes < ctx->size) - ctx->bytes += scnprintf(ctx->buf + ctx->bytes, - ctx->size - ctx->bytes, - "%02x ", (unsigned) pl[pl_itr++]); - if (ctx->bytes < ctx->size) - ctx->buf[ctx->bytes++] = '\n'; - return 0; -} -EXPORT_SYMBOL_GPL(uwb_ie_dump_hex); - - -/** - * Verify that a pointer in a buffer points to valid IE - * - * @start: pointer to start of buffer in which IE appears - * @itr: pointer to IE inside buffer that will be verified - * @top: pointer to end of buffer - * - * @returns: 0 if IE is valid, <0 otherwise - * - * Verification involves checking that the buffer can contain a - * header and the amount of data reported in the IE header can be found in - * the buffer. - */ -static -int uwb_rc_ie_verify(struct uwb_dev *uwb_dev, const void *start, - const void *itr, const void *top) -{ - struct device *dev = &uwb_dev->dev; - const struct uwb_ie_hdr *ie_hdr; - - if (top - itr < sizeof(*ie_hdr)) { - dev_err(dev, "Bad IE: no data to decode header " - "(%zu bytes left vs %zu needed) at offset %zu\n", - top - itr, sizeof(*ie_hdr), itr - start); - return -EINVAL; - } - ie_hdr = itr; - itr += sizeof(*ie_hdr); - if (top - itr < ie_hdr->length) { - dev_err(dev, "Bad IE: not enough data for payload " - "(%zu bytes left vs %zu needed) at offset %zu\n", - top - itr, (size_t)ie_hdr->length, - (void *)ie_hdr - start); return -EINVAL; } - return 0; -} - -/** - * Walk a buffer filled with consecutive IE's a buffer - * - * @uwb_dev: UWB device this IEs belong to (for err messages mainly) - * - * @fn: function to call with each IE; if it returns 0, we keep - * traversing the buffer. If it returns !0, we'll stop and return - * that value. - * - * @data: pointer passed to @fn - * - * @buf: buffer where the consecutive IEs are located - * - * @size: size of @buf - * - * Each IE is checked for basic correctness (there is space left for - * the header and the payload). If that test is failed, we stop - * processing. For every good IE, @fn is called. - */ -ssize_t uwb_ie_for_each(struct uwb_dev *uwb_dev, uwb_ie_f fn, void *data, - const void *buf, size_t size) -{ - ssize_t result = 0; - const struct uwb_ie_hdr *ie_hdr; - const void *itr = buf, *top = itr + size; - - while (itr < top) { - if (uwb_rc_ie_verify(uwb_dev, buf, itr, top) != 0) - break; - ie_hdr = itr; - itr += sizeof(*ie_hdr) + ie_hdr->length; - result = fn(uwb_dev, ie_hdr, itr - buf, data); - if (result != 0) - break; - } + *pget_ie = get_ie; return result; } -EXPORT_SYMBOL_GPL(uwb_ie_for_each); /** @@ -256,70 +178,6 @@ error_cmd: return result; } -/** - * Determine by IE id if IE is host settable - * WUSB 1.0 [8.6.2.8 Table 8.85] - * - * EXCEPTION: - * All but UWB_IE_WLP appears in Table 8.85 from WUSB 1.0. Setting this IE - * is required for the WLP substack to perform association with its WSS so - * we hope that the WUSB spec will be changed to reflect this. - */ -static -int uwb_rc_ie_is_host_settable(enum uwb_ie element_id) -{ - if (element_id == UWB_PCA_AVAILABILITY || - element_id == UWB_BP_SWITCH_IE || - element_id == UWB_MAC_CAPABILITIES_IE || - element_id == UWB_PHY_CAPABILITIES_IE || - element_id == UWB_APP_SPEC_PROBE_IE || - element_id == UWB_IDENTIFICATION_IE || - element_id == UWB_MASTER_KEY_ID_IE || - element_id == UWB_IE_WLP || - element_id == UWB_APP_SPEC_IE) - return 1; - return 0; -} - - -/** - * Extract Host Settable IEs from IE - * - * @ie_data: pointer to buffer containing all IEs - * @size: size of buffer - * - * @returns: length of buffer that only includes host settable IEs - * - * Given a buffer of IEs we move all Host Settable IEs to front of buffer - * by overwriting the IEs that are not Host Settable. - * Buffer length is adjusted accordingly. - */ -static -ssize_t uwb_rc_parse_host_settable_ie(struct uwb_dev *uwb_dev, - void *ie_data, size_t size) -{ - size_t new_len = size; - struct uwb_ie_hdr *ie_hdr; - size_t ie_length; - void *itr = ie_data, *top = itr + size; - - while (itr < top) { - if (uwb_rc_ie_verify(uwb_dev, ie_data, itr, top) != 0) - break; - ie_hdr = itr; - ie_length = sizeof(*ie_hdr) + ie_hdr->length; - if (uwb_rc_ie_is_host_settable(ie_hdr->element_id)) { - itr += ie_length; - } else { - memmove(itr, itr + ie_length, top - (itr + ie_length)); - new_len -= ie_length; - top -= ie_length; - } - } - return new_len; -} - - /* Cleanup the whole IE management subsystem */ void uwb_rc_ie_init(struct uwb_rc *uwb_rc) { @@ -328,49 +186,34 @@ void uwb_rc_ie_init(struct uwb_rc *uwb_rc) /** - * Set up cache for host settable IEs currently being transmitted + * uwb_rc_ie_setup - setup a radio controller's IE manager + * @uwb_rc: the radio controller. * - * First we just call GET-IE to get the current IEs being transmitted - * (or we workaround and pretend we did) and (because the format is - * the same) reuse that as the IE cache (with the command prefix, as - * explained in 'struct uwb_rc'). + * The current set of IEs are obtained from the hardware with a GET-IE + * command (since the radio controller is not yet beaconing this will + * be just the hardware's MAC and PHY Capability IEs). * - * @returns: size of cache created + * Returns 0 on success; -ve on an error. */ -ssize_t uwb_rc_ie_setup(struct uwb_rc *uwb_rc) +int uwb_rc_ie_setup(struct uwb_rc *uwb_rc) { - struct device *dev = &uwb_rc->uwb_dev.dev; - ssize_t result; - size_t capacity; - struct uwb_rc_evt_get_ie *ie_info; + struct uwb_rc_evt_get_ie *ie_info = NULL; + int capacity; + + capacity = uwb_rc_get_ie(uwb_rc, &ie_info); + if (capacity < 0) + return capacity; - d_fnstart(3, dev, "(%p)\n", uwb_rc); mutex_lock(&uwb_rc->ies_mutex); - result = uwb_rc_get_ie(uwb_rc, &ie_info); - if (result < 0) - goto error_get_ie; - capacity = result; - d_printf(5, dev, "Got IEs %zu bytes (%zu long at %p)\n", result, - (size_t)le16_to_cpu(ie_info->wIELength), ie_info); - - /* Remove IEs that host should not set. */ - result = uwb_rc_parse_host_settable_ie(&uwb_rc->uwb_dev, - ie_info->IEData, le16_to_cpu(ie_info->wIELength)); - if (result < 0) - goto error_parse; - d_printf(5, dev, "purged non-settable IEs to %zu bytes\n", result); - uwb_rc->ies = (void *) ie_info; + + uwb_rc->ies = (struct uwb_rc_cmd_set_ie *)ie_info; uwb_rc->ies->rccb.bCommandType = UWB_RC_CET_GENERAL; uwb_rc->ies->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SET_IE); uwb_rc->ies_capacity = capacity; - d_printf(5, dev, "IE cache at %p %zu bytes, %zu capacity\n", - ie_info, result, capacity); - result = 0; -error_parse: -error_get_ie: + mutex_unlock(&uwb_rc->ies_mutex); - d_fnend(3, dev, "(%p) = %zu\n", uwb_rc, result); - return result; + + return 0; } @@ -383,26 +226,47 @@ void uwb_rc_ie_release(struct uwb_rc *uwb_rc) } -static -int __acc_size(struct uwb_dev *uwb_dev, const struct uwb_ie_hdr *ie_hdr, - size_t offset, void *_ctx) +static int uwb_rc_ie_add_one(struct uwb_rc *rc, const struct uwb_ie_hdr *new_ie) { - size_t *acc_size = _ctx; - *acc_size += sizeof(*ie_hdr) + ie_hdr->length; - d_printf(6, &uwb_dev->dev, "new acc size %zu\n", *acc_size); + struct uwb_rc_cmd_set_ie *new_ies; + void *ptr, *prev_ie; + struct uwb_ie_hdr *ie; + size_t length, new_ie_len, new_capacity, size, prev_size; + + length = le16_to_cpu(rc->ies->wIELength); + new_ie_len = sizeof(struct uwb_ie_hdr) + new_ie->length; + new_capacity = sizeof(struct uwb_rc_cmd_set_ie) + length + new_ie_len; + + if (new_capacity > rc->ies_capacity) { + new_ies = krealloc(rc->ies, new_capacity, GFP_KERNEL); + if (!new_ies) + return -ENOMEM; + rc->ies = new_ies; + } + + ptr = rc->ies->IEData; + size = length; + for (;;) { + prev_ie = ptr; + prev_size = size; + ie = uwb_ie_next(&ptr, &size); + if (!ie || ie->element_id > new_ie->element_id) + break; + } + + memmove(prev_ie + new_ie_len, prev_ie, prev_size); + memcpy(prev_ie, new_ie, new_ie_len); + rc->ies->wIELength = cpu_to_le16(length + new_ie_len); + return 0; } - /** - * Add a new IE to IEs currently being transmitted by device - * + * uwb_rc_ie_add - add new IEs to the radio controller's beacon + * @uwb_rc: the radio controller. * @ies: the buffer containing the new IE or IEs to be added to - * the device's beacon. The buffer will be verified for - * consistence (meaning the headers should be right) and - * consistent with the buffer size. - * @size: size of @ies (in bytes, total buffer size) - * @returns: 0 if ok, <0 errno code on error + * the device's beacon. + * @size: length of all the IEs. * * According to WHCI 0.95 [4.13.6] the driver will only receive the RCEB * after the device sent the first beacon that includes the IEs specified @@ -411,66 +275,40 @@ int __acc_size(struct uwb_dev *uwb_dev, const struct uwb_ie_hdr *ie_hdr, * we start beaconing. * * Setting an IE on the device will overwrite all current IEs in device. So - * we take the current IEs being transmitted by the device, append the + * we take the current IEs being transmitted by the device, insert the * new one, and call SET IE with all the IEs needed. * - * The local IE cache will only be updated with the new IE if SET IE - * completed successfully. + * Returns 0 on success; or -ENOMEM. */ int uwb_rc_ie_add(struct uwb_rc *uwb_rc, const struct uwb_ie_hdr *ies, size_t size) { int result = 0; - struct device *dev = &uwb_rc->uwb_dev.dev; - struct uwb_rc_cmd_set_ie *new_ies; - size_t ies_size, total_size, acc_size = 0; - - if (uwb_rc->ies == NULL) - return -ESHUTDOWN; - uwb_ie_for_each(&uwb_rc->uwb_dev, __acc_size, &acc_size, ies, size); - if (acc_size != size) { - dev_err(dev, "BUG: bad IEs, misconstructed headers " - "[%zu bytes reported vs %zu calculated]\n", - size, acc_size); - WARN_ON(1); - return -EINVAL; - } + void *ptr; + const struct uwb_ie_hdr *ie; + mutex_lock(&uwb_rc->ies_mutex); - ies_size = le16_to_cpu(uwb_rc->ies->wIELength); - total_size = sizeof(*uwb_rc->ies) + ies_size; - if (total_size + size > uwb_rc->ies_capacity) { - d_printf(4, dev, "Reallocating IE cache from %p capacity %zu " - "to capacity %zu\n", uwb_rc->ies, uwb_rc->ies_capacity, - total_size + size); - new_ies = kzalloc(total_size + size, GFP_KERNEL); - if (new_ies == NULL) { - dev_err(dev, "No memory for adding new IE\n"); - result = -ENOMEM; - goto error_alloc; - } - memcpy(new_ies, uwb_rc->ies, total_size); - uwb_rc->ies_capacity = total_size + size; - kfree(uwb_rc->ies); - uwb_rc->ies = new_ies; - d_printf(4, dev, "New IE cache at %p capacity %zu\n", - uwb_rc->ies, uwb_rc->ies_capacity); + + ptr = (void *)ies; + for (;;) { + ie = uwb_ie_next(&ptr, &size); + if (!ie) + break; + + result = uwb_rc_ie_add_one(uwb_rc, ie); + if (result < 0) + break; } - memcpy((void *)uwb_rc->ies + total_size, ies, size); - uwb_rc->ies->wIELength = cpu_to_le16(ies_size + size); - if (uwb_rc->beaconing != -1) { - result = uwb_rc_set_ie(uwb_rc, uwb_rc->ies); - if (result < 0) { - dev_err(dev, "Cannot set new IE on device: %d\n", - result); - uwb_rc->ies->wIELength = cpu_to_le16(ies_size); + if (result >= 0) { + if (size == 0) { + if (uwb_rc->beaconing != -1) + result = uwb_rc_set_ie(uwb_rc, uwb_rc->ies); } else - result = 0; + result = -EINVAL; } - d_printf(4, dev, "IEs now occupy %hu bytes of %zu capacity at %p\n", - le16_to_cpu(uwb_rc->ies->wIELength), uwb_rc->ies_capacity, - uwb_rc->ies); -error_alloc: + mutex_unlock(&uwb_rc->ies_mutex); + return result; } EXPORT_SYMBOL_GPL(uwb_rc_ie_add); @@ -489,53 +327,52 @@ EXPORT_SYMBOL_GPL(uwb_rc_ie_add); * beacon. We don't reallocate, we just mark the size smaller. */ static -int uwb_rc_ie_cache_rm(struct uwb_rc *uwb_rc, enum uwb_ie to_remove) +void uwb_rc_ie_cache_rm(struct uwb_rc *uwb_rc, enum uwb_ie to_remove) { - struct uwb_ie_hdr *ie_hdr; - size_t new_len = le16_to_cpu(uwb_rc->ies->wIELength); - void *itr = uwb_rc->ies->IEData; - void *top = itr + new_len; - - while (itr < top) { - ie_hdr = itr; - if (ie_hdr->element_id != to_remove) { - itr += sizeof(*ie_hdr) + ie_hdr->length; - } else { - int ie_length; - ie_length = sizeof(*ie_hdr) + ie_hdr->length; - if (top - itr != ie_length) - memmove(itr, itr + ie_length, top - itr + ie_length); - top -= ie_length; - new_len -= ie_length; + struct uwb_ie_hdr *ie; + size_t len = le16_to_cpu(uwb_rc->ies->wIELength); + void *ptr; + size_t size; + + ptr = uwb_rc->ies->IEData; + size = len; + for (;;) { + ie = uwb_ie_next(&ptr, &size); + if (!ie) + break; + if (ie->element_id == to_remove) { + len -= sizeof(struct uwb_ie_hdr) + ie->length; + memmove(ie, ptr, size); + ptr = ie; } } - uwb_rc->ies->wIELength = cpu_to_le16(new_len); - return 0; + uwb_rc->ies->wIELength = cpu_to_le16(len); } /** - * Remove an IE currently being transmitted by device + * uwb_rc_ie_rm - remove an IE from the radio controller's beacon + * @uwb_rc: the radio controller. + * @element_id: the element ID of the IE to remove. * - * @element_id: id of IE to be removed from device's beacon + * Only IEs previously added with uwb_rc_ie_add() may be removed. + * + * Returns 0 on success; or -ve the SET-IE command to the radio + * controller failed. */ int uwb_rc_ie_rm(struct uwb_rc *uwb_rc, enum uwb_ie element_id) { - struct device *dev = &uwb_rc->uwb_dev.dev; - int result; + int result = 0; - if (uwb_rc->ies == NULL) - return -ESHUTDOWN; mutex_lock(&uwb_rc->ies_mutex); - result = uwb_rc_ie_cache_rm(uwb_rc, element_id); - if (result < 0) - dev_err(dev, "Cannot remove IE from cache.\n"); - if (uwb_rc->beaconing != -1) { + + uwb_rc_ie_cache_rm(uwb_rc, element_id); + + if (uwb_rc->beaconing != -1) result = uwb_rc_set_ie(uwb_rc, uwb_rc->ies); - if (result < 0) - dev_err(dev, "Cannot set new IE on device.\n"); - } + mutex_unlock(&uwb_rc->ies_mutex); + return result; } EXPORT_SYMBOL_GPL(uwb_rc_ie_rm); diff --git a/drivers/uwb/lc-rc.c b/drivers/uwb/lc-rc.c index ee5772f00d42..1129e8767b58 100644 --- a/drivers/uwb/lc-rc.c +++ b/drivers/uwb/lc-rc.c @@ -468,28 +468,3 @@ void uwb_rc_put(struct uwb_rc *rc) __uwb_rc_put(rc); } EXPORT_SYMBOL_GPL(uwb_rc_put); - -/* - * - * - */ -ssize_t uwb_rc_print_IEs(struct uwb_rc *uwb_rc, char *buf, size_t size) -{ - ssize_t result; - struct uwb_rc_evt_get_ie *ie_info; - struct uwb_buf_ctx ctx; - - result = uwb_rc_get_ie(uwb_rc, &ie_info); - if (result < 0) - goto error_get_ie; - ctx.buf = buf; - ctx.size = size; - ctx.bytes = 0; - uwb_ie_for_each(&uwb_rc->uwb_dev, uwb_ie_dump_hex, &ctx, - ie_info->IEData, result - sizeof(*ie_info)); - result = ctx.bytes; - kfree(ie_info); -error_get_ie: - return result; -} - diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h index 2ad307d12961..983ebc4dd8d5 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/uwb/uwb-internal.h @@ -66,14 +66,14 @@ extern int uwb_rc_scan(struct uwb_rc *rc, unsigned channel, enum uwb_scan_type type, unsigned bpst_offset); extern int uwb_rc_send_all_drp_ie(struct uwb_rc *rc); -extern ssize_t uwb_rc_print_IEs(struct uwb_rc *rc, char *, size_t); -extern void uwb_rc_ie_init(struct uwb_rc *); -extern void uwb_rc_ie_init(struct uwb_rc *); -extern ssize_t uwb_rc_ie_setup(struct uwb_rc *); -extern void uwb_rc_ie_release(struct uwb_rc *); -extern int uwb_rc_ie_add(struct uwb_rc *, - const struct uwb_ie_hdr *, size_t); -extern int uwb_rc_ie_rm(struct uwb_rc *, enum uwb_ie); + +void uwb_rc_ie_init(struct uwb_rc *); +int uwb_rc_ie_setup(struct uwb_rc *); +void uwb_rc_ie_release(struct uwb_rc *); +int uwb_ie_dump_hex(const struct uwb_ie_hdr *ies, size_t len, + char *buf, size_t size); +int uwb_rc_set_ie(struct uwb_rc *, struct uwb_rc_cmd_set_ie *); + extern const char *uwb_rc_strerror(unsigned code); diff --git a/drivers/uwb/wlp/wlp-internal.h b/drivers/uwb/wlp/wlp-internal.h index 1c94fabfb1a7..3e8d5de7c5b9 100644 --- a/drivers/uwb/wlp/wlp-internal.h +++ b/drivers/uwb/wlp/wlp-internal.h @@ -42,10 +42,6 @@ enum wlp_wss_connect { extern struct kobj_type wss_ktype; extern struct attribute_group wss_attr_group; -extern int uwb_rc_ie_add(struct uwb_rc *, const struct uwb_ie_hdr *, size_t); -extern int uwb_rc_ie_rm(struct uwb_rc *, enum uwb_ie); - - /* This should be changed to a dynamic array where entries are sorted * by eth_addr and search is done in a binary form * diff --git a/include/linux/uwb.h b/include/linux/uwb.h index 010ee708304d..6d93f54b8879 100644 --- a/include/linux/uwb.h +++ b/include/linux/uwb.h @@ -444,7 +444,6 @@ ssize_t uwb_rc_vcmd(struct uwb_rc *rc, const char *cmd_name, struct uwb_rccb *cmd, size_t cmd_size, u8 expected_type, u16 expected_event, struct uwb_rceb **preply); -ssize_t uwb_rc_get_ie(struct uwb_rc *, struct uwb_rc_evt_get_ie **); int uwb_bg_joined(struct uwb_rc *rc); size_t __uwb_addr_print(char *, size_t, const unsigned char *, int); @@ -653,22 +652,9 @@ static inline int edc_inc(struct edc *err_hist, u16 max_err, u16 timeframe) /* Information Element handling */ -/* For representing the state of writing to a buffer when iterating */ -struct uwb_buf_ctx { - char *buf; - size_t bytes, size; -}; - -typedef int (*uwb_ie_f)(struct uwb_dev *, const struct uwb_ie_hdr *, - size_t, void *); struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len); -ssize_t uwb_ie_for_each(struct uwb_dev *uwb_dev, uwb_ie_f fn, void *data, - const void *buf, size_t size); -int uwb_ie_dump_hex(struct uwb_dev *, const struct uwb_ie_hdr *, - size_t, void *); -int uwb_rc_set_ie(struct uwb_rc *, struct uwb_rc_cmd_set_ie *); -struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len); - +int uwb_rc_ie_add(struct uwb_rc *uwb_rc, const struct uwb_ie_hdr *ies, size_t size); +int uwb_rc_ie_rm(struct uwb_rc *uwb_rc, enum uwb_ie element_id); /* * Transmission statistics -- cgit v1.2.3 From ae9eba0e2744f1aa15cdc97cd39277a84723ae23 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Thu, 30 Oct 2008 20:06:16 +0100 Subject: uwb: struct device - replace bus_id with dev_name(), dev_set_name() Cc: David Vrabel Acked-by: Greg Kroah-Hartman Signed-Off-By: Kay Sievers Signed-off-by: David Vrabel --- drivers/uwb/umc-dev.c | 3 +-- drivers/uwb/whci.c | 2 +- include/linux/uwb/debug.h | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/uwb/umc-dev.c b/drivers/uwb/umc-dev.c index aa44e1c1a102..53207e14cd8f 100644 --- a/drivers/uwb/umc-dev.c +++ b/drivers/uwb/umc-dev.c @@ -31,8 +31,7 @@ struct umc_dev *umc_device_create(struct device *parent, int n) umc = kzalloc(sizeof(struct umc_dev), GFP_KERNEL); if (umc) { - snprintf(umc->dev.bus_id, sizeof(umc->dev.bus_id), "%s-%d", - parent->bus_id, n); + dev_set_name(&umc->dev, "%s-%d", dev_name(parent), n); umc->dev.parent = parent; umc->dev.bus = &umc_bus_type; umc->dev.release = umc_device_release; diff --git a/drivers/uwb/whci.c b/drivers/uwb/whci.c index 3df2388f908f..e626467f95e3 100644 --- a/drivers/uwb/whci.c +++ b/drivers/uwb/whci.c @@ -111,7 +111,7 @@ static int whci_add_cap(struct whci_card *card, int n) + UWBCAPDATA_TO_OFFSET(capdata); umc->resource.end = umc->resource.start + (n == 0 ? 0x20 : UWBCAPDATA_TO_SIZE(capdata)) - 1; - umc->resource.name = umc->dev.bus_id; + umc->resource.name = dev_name(&umc->dev); umc->resource.flags = card->pci->resource[bar].flags; umc->resource.parent = &card->pci->resource[bar]; umc->irq = card->pci->irq; diff --git a/include/linux/uwb/debug.h b/include/linux/uwb/debug.h index a86a73fe303f..67a240527145 100644 --- a/include/linux/uwb/debug.h +++ b/include/linux/uwb/debug.h @@ -60,7 +60,7 @@ do { \ snprintf(__head, sizeof(__head), \ "%s %s: ", \ dev_driver_string(__dev), \ - __dev->bus_id); \ + dev_name(__dev)); \ } \ printk(KERN_ERR "%s%s" _tag ": " f, __head, \ __func__, ## a); \ -- cgit v1.2.3 From c5995bd2819dc577d0b32b26be0836d16c977e24 Mon Sep 17 00:00:00 2001 From: Stefano Panella Date: Tue, 4 Nov 2008 14:06:31 +0000 Subject: uwb: infrastructure for handling Relinquish Request IEs The structures and event handler needed to handle Relinish Request IEs received from neighbors. Nothing is done with these IEs yet. Signed-off-by: Stefano Panella Signed-off-by: David Vrabel --- drivers/uwb/Makefile | 1 + drivers/uwb/ie-rcv.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/uwb/uwb-internal.h | 1 + drivers/uwb/uwbd.c | 4 ++++ include/linux/uwb/spec.h | 28 +++++++++++++++++++++++ 5 files changed, 89 insertions(+) create mode 100644 drivers/uwb/ie-rcv.c (limited to 'include/linux') diff --git a/drivers/uwb/Makefile b/drivers/uwb/Makefile index 257e6908304c..2b99c3e61671 100644 --- a/drivers/uwb/Makefile +++ b/drivers/uwb/Makefile @@ -13,6 +13,7 @@ uwb-objs := \ drp-ie.o \ est.o \ ie.o \ + ie-rcv.o \ lc-dev.o \ lc-rc.o \ neh.o \ diff --git a/drivers/uwb/ie-rcv.c b/drivers/uwb/ie-rcv.c new file mode 100644 index 000000000000..917e6d78a798 --- /dev/null +++ b/drivers/uwb/ie-rcv.c @@ -0,0 +1,55 @@ +/* + * Ultra Wide Band + * IE Received notification handling. + * + * Copyright (C) 2008 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include "uwb-internal.h" + +/* + * Process an incoming IE Received notification. + */ +int uwbd_evt_handle_rc_ie_rcv(struct uwb_event *evt) +{ + int result = -EINVAL; + struct device *dev = &evt->rc->uwb_dev.dev; + struct uwb_rc_evt_ie_rcv *iercv; + size_t iesize; + + /* Is there enough data to decode it? */ + if (evt->notif.size < sizeof(*iercv)) { + dev_err(dev, "IE Received notification: Not enough data to " + "decode (%zu vs %zu bytes needed)\n", + evt->notif.size, sizeof(*iercv)); + goto error; + } + iercv = container_of(evt->notif.rceb, struct uwb_rc_evt_ie_rcv, rceb); + iesize = le16_to_cpu(iercv->wIELength); + + dev_dbg(dev, "IE received, element ID=%d\n", iercv->IEData[0]); + + if (iercv->IEData[0] == UWB_RELINQUISH_REQUEST_IE) { + dev_warn(dev, "unhandled Relinquish Request IE\n"); + } + + return 0; +error: + return result; +} diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h index 983ebc4dd8d5..031e8a885681 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/uwb/uwb-internal.h @@ -167,6 +167,7 @@ extern void uwbd_event_queue(struct uwb_event *); void uwbd_flush(struct uwb_rc *rc); /* UWB event handlers */ +extern int uwbd_evt_handle_rc_ie_rcv(struct uwb_event *); extern int uwbd_evt_handle_rc_beacon(struct uwb_event *); extern int uwbd_evt_handle_rc_beacon_size(struct uwb_event *); extern int uwbd_evt_handle_rc_bpoie_change(struct uwb_event *); diff --git a/drivers/uwb/uwbd.c b/drivers/uwb/uwbd.c index 78908416e42c..f75113571f4a 100644 --- a/drivers/uwb/uwbd.c +++ b/drivers/uwb/uwbd.c @@ -104,6 +104,10 @@ struct uwbd_event { /** Table of handlers for and properties of the UWBD Radio Control Events */ static struct uwbd_event uwbd_events[] = { + [UWB_RC_EVT_IE_RCV] = { + .handler = uwbd_evt_handle_rc_ie_rcv, + .name = "IE_RECEIVED" + }, [UWB_RC_EVT_BEACON] = { .handler = uwbd_evt_handle_rc_beacon, .name = "BEACON_RECEIVED" diff --git a/include/linux/uwb/spec.h b/include/linux/uwb/spec.h index 198c15f8e251..a30436ea53aa 100644 --- a/include/linux/uwb/spec.h +++ b/include/linux/uwb/spec.h @@ -200,6 +200,12 @@ enum uwb_drp_reason { UWB_DRP_REASON_MODIFIED, }; +/** Relinquish Request Reason Codes ([ECMA-368] table 113) */ +enum uwb_relinquish_req_reason { + UWB_RELINQUISH_REQ_REASON_NON_SPECIFIC = 0, + UWB_RELINQUISH_REQ_REASON_OVER_ALLOCATION, +}; + /** * DRP Notification Reason Codes (WHCI 0.95 [3.1.4.9]) */ @@ -252,6 +258,7 @@ enum uwb_ie { UWB_APP_SPEC_PROBE_IE = 15, UWB_IDENTIFICATION_IE = 19, UWB_MASTER_KEY_ID_IE = 20, + UWB_RELINQUISH_REQUEST_IE = 21, UWB_IE_WLP = 250, /* WiMedia Logical Link Control Protocol WLP 0.99 */ UWB_APP_SPEC_IE = 255, }; @@ -365,6 +372,27 @@ struct uwb_ie_drp_avail { DECLARE_BITMAP(bmp, UWB_NUM_MAS); } __attribute__((packed)); +/* Relinqish Request IE ([ECMA-368] section 16.8.19). */ +struct uwb_relinquish_request_ie { + struct uwb_ie_hdr hdr; + __le16 relinquish_req_control; + struct uwb_dev_addr dev_addr; + struct uwb_drp_alloc allocs[]; +} __attribute__((packed)); + +static inline int uwb_ie_relinquish_req_reason_code(struct uwb_relinquish_request_ie *ie) +{ + return (le16_to_cpu(ie->relinquish_req_control) >> 0) & 0xf; +} + +static inline void uwb_ie_relinquish_req_set_reason_code(struct uwb_relinquish_request_ie *ie, + int reason_code) +{ + u16 ctrl = le16_to_cpu(ie->relinquish_req_control); + ctrl = (ctrl & ~(0xf << 0)) | (reason_code << 0); + ie->relinquish_req_control = cpu_to_le16(ctrl); +} + /** * The Vendor ID is set to an OUI that indicates the vendor of the device. * ECMA-368 [16.8.10] -- cgit v1.2.3 From 6d5a681dfb583b2f1eefe7cd5505419ca2d4d6c8 Mon Sep 17 00:00:00 2001 From: Stefano Panella Date: Tue, 4 Nov 2008 14:24:57 +0000 Subject: uwb: add commands to add/remove IEs to the debug interface Add the commands UWB_DBG_CMD_IE_ADD and UWB_DBG_CMD_IE_RM to the debug interface and make them call uwb_rc_ie_add() and uwb_rc_ie_rm(). Signed-off-by: Stefano Panella Signed-off-by: David Vrabel --- drivers/uwb/uwb-debug.c | 20 +++++++++++++++++++- include/linux/uwb/debug-cmd.h | 9 +++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c index 6db641e45313..88e6ac713817 100644 --- a/drivers/uwb/uwb-debug.c +++ b/drivers/uwb/uwb-debug.c @@ -168,6 +168,18 @@ static int cmd_rsv_terminate(struct uwb_rc *rc, return 0; } +static int cmd_ie_add(struct uwb_rc *rc, struct uwb_dbg_cmd_ie *ie_to_add) +{ + return uwb_rc_ie_add(rc, + (const struct uwb_ie_hdr *) ie_to_add->data, + ie_to_add->len); +} + +static int cmd_ie_rm(struct uwb_rc *rc, struct uwb_dbg_cmd_ie *ie_to_rm) +{ + return uwb_rc_ie_rm(rc, ie_to_rm->data[0]); +} + static int command_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; @@ -195,6 +207,12 @@ static ssize_t command_write(struct file *file, const char __user *buf, case UWB_DBG_CMD_RSV_TERMINATE: ret = cmd_rsv_terminate(rc, &cmd.rsv_terminate); break; + case UWB_DBG_CMD_IE_ADD: + ret = cmd_ie_add(rc, &cmd.ie_add); + break; + case UWB_DBG_CMD_IE_RM: + ret = cmd_ie_rm(rc, &cmd.ie_rm); + break; default: return -EINVAL; } @@ -332,7 +350,7 @@ void uwb_dbg_add_rc(struct uwb_rc *rc) } /** - * uwb_dbg_add_rc - remove a radio controller's debug interface + * uwb_dbg_del_rc - remove a radio controller's debug interface * @rc: the radio controller */ void uwb_dbg_del_rc(struct uwb_rc *rc) diff --git a/include/linux/uwb/debug-cmd.h b/include/linux/uwb/debug-cmd.h index 1141f41bab5c..6a16566f0221 100644 --- a/include/linux/uwb/debug-cmd.h +++ b/include/linux/uwb/debug-cmd.h @@ -32,6 +32,8 @@ enum uwb_dbg_cmd_type { UWB_DBG_CMD_RSV_ESTABLISH = 1, UWB_DBG_CMD_RSV_TERMINATE = 2, + UWB_DBG_CMD_IE_ADD = 3, + UWB_DBG_CMD_IE_RM = 4, }; struct uwb_dbg_cmd_rsv_establish { @@ -46,11 +48,18 @@ struct uwb_dbg_cmd_rsv_terminate { int index; }; +struct uwb_dbg_cmd_ie { + __u8 data[128]; + int len; +}; + struct uwb_dbg_cmd { __u32 type; union { struct uwb_dbg_cmd_rsv_establish rsv_establish; struct uwb_dbg_cmd_rsv_terminate rsv_terminate; + struct uwb_dbg_cmd_ie ie_add; + struct uwb_dbg_cmd_ie ie_rm; }; }; -- cgit v1.2.3 From fec1a5932f16c0eb1b3f5ca2e18d81d860924088 Mon Sep 17 00:00:00 2001 From: Stefano Panella Date: Tue, 4 Nov 2008 15:39:08 +0000 Subject: uwb: per-radio controller event thread and beacon cache Use an event thread per-radio controller so processing events from one radio controller doesn't delay another. A radio controller shouldn't have information on devices seen by a different radio controller (they may be on different channels) so make the beacon cache per-radio controller. Signed-off-by: Stefano Panella Signed-off-by: David Vrabel --- drivers/uwb/beacon.c | 61 ++++++++++++++--------------- drivers/uwb/driver.c | 2 - drivers/uwb/lc-rc.c | 21 ++++++---- drivers/uwb/uwb-internal.h | 20 ++-------- drivers/uwb/uwbd.c | 98 +++++++++++++++------------------------------- include/linux/uwb.h | 20 ++++++++++ 6 files changed, 100 insertions(+), 122 deletions(-) (limited to 'include/linux') diff --git a/drivers/uwb/beacon.c b/drivers/uwb/beacon.c index ad823987cede..d9f2a8acc593 100644 --- a/drivers/uwb/beacon.c +++ b/drivers/uwb/beacon.c @@ -168,12 +168,6 @@ out_up: * FIXME: use something faster for search than a list */ -struct uwb_beca uwb_beca = { - .list = LIST_HEAD_INIT(uwb_beca.list), - .mutex = __MUTEX_INITIALIZER(uwb_beca.mutex) -}; - - void uwb_bce_kfree(struct kref *_bce) { struct uwb_beca_e *bce = container_of(_bce, struct uwb_beca_e, refcnt); @@ -185,10 +179,11 @@ void uwb_bce_kfree(struct kref *_bce) /* Find a beacon by dev addr in the cache */ static -struct uwb_beca_e *__uwb_beca_find_bydev(const struct uwb_dev_addr *dev_addr) +struct uwb_beca_e *__uwb_beca_find_bydev(struct uwb_rc *rc, + const struct uwb_dev_addr *dev_addr) { struct uwb_beca_e *bce, *next; - list_for_each_entry_safe(bce, next, &uwb_beca.list, node) { + list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) { d_printf(6, NULL, "looking for addr %02x:%02x in %02x:%02x\n", dev_addr->data[0], dev_addr->data[1], bce->dev_addr.data[0], bce->dev_addr.data[1]); @@ -202,10 +197,11 @@ out: /* Find a beacon by dev addr in the cache */ static -struct uwb_beca_e *__uwb_beca_find_bymac(const struct uwb_mac_addr *mac_addr) +struct uwb_beca_e *__uwb_beca_find_bymac(struct uwb_rc *rc, + const struct uwb_mac_addr *mac_addr) { struct uwb_beca_e *bce, *next; - list_for_each_entry_safe(bce, next, &uwb_beca.list, node) { + list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) { if (!memcmp(bce->mac_addr, mac_addr->data, sizeof(struct uwb_mac_addr))) goto out; @@ -229,11 +225,11 @@ struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc, struct uwb_dev *found = NULL; struct uwb_beca_e *bce; - mutex_lock(&uwb_beca.mutex); - bce = __uwb_beca_find_bydev(devaddr); + mutex_lock(&rc->uwb_beca.mutex); + bce = __uwb_beca_find_bydev(rc, devaddr); if (bce) found = uwb_dev_try_get(rc, bce->uwb_dev); - mutex_unlock(&uwb_beca.mutex); + mutex_unlock(&rc->uwb_beca.mutex); return found; } @@ -249,11 +245,11 @@ struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc, struct uwb_dev *found = NULL; struct uwb_beca_e *bce; - mutex_lock(&uwb_beca.mutex); - bce = __uwb_beca_find_bymac(macaddr); + mutex_lock(&rc->uwb_beca.mutex); + bce = __uwb_beca_find_bymac(rc, macaddr); if (bce) found = uwb_dev_try_get(rc, bce->uwb_dev); - mutex_unlock(&uwb_beca.mutex); + mutex_unlock(&rc->uwb_beca.mutex); return found; } @@ -274,7 +270,9 @@ static void uwb_beca_e_init(struct uwb_beca_e *bce) * @bf: Beacon frame (part of b, really) * @ts_jiffies: Timestamp (in jiffies) when the beacon was received */ -struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *be, +static +struct uwb_beca_e *__uwb_beca_add(struct uwb_rc *rc, + struct uwb_rc_evt_beacon *be, struct uwb_beacon_frame *bf, unsigned long ts_jiffies) { @@ -286,7 +284,7 @@ struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *be, uwb_beca_e_init(bce); bce->ts_jiffies = ts_jiffies; bce->uwb_dev = NULL; - list_add(&bce->node, &uwb_beca.list); + list_add(&bce->node, &rc->uwb_beca.list); return bce; } @@ -295,13 +293,13 @@ struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *be, * * Remove associated devicest too. */ -void uwb_beca_purge(void) +void uwb_beca_purge(struct uwb_rc *rc) { struct uwb_beca_e *bce, *next; unsigned long expires; - mutex_lock(&uwb_beca.mutex); - list_for_each_entry_safe(bce, next, &uwb_beca.list, node) { + mutex_lock(&rc->uwb_beca.mutex); + list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) { expires = bce->ts_jiffies + msecs_to_jiffies(beacon_timeout_ms); if (time_after(jiffies, expires)) { uwbd_dev_offair(bce); @@ -309,19 +307,20 @@ void uwb_beca_purge(void) uwb_bce_put(bce); } } - mutex_unlock(&uwb_beca.mutex); + mutex_unlock(&rc->uwb_beca.mutex); } /* Clean up the whole beacon cache. Called on shutdown */ -void uwb_beca_release(void) +void uwb_beca_release(struct uwb_rc *rc) { struct uwb_beca_e *bce, *next; - mutex_lock(&uwb_beca.mutex); - list_for_each_entry_safe(bce, next, &uwb_beca.list, node) { + + mutex_lock(&rc->uwb_beca.mutex); + list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) { list_del(&bce->node); uwb_bce_put(bce); } - mutex_unlock(&uwb_beca.mutex); + mutex_unlock(&rc->uwb_beca.mutex); } static void uwb_beacon_print(struct uwb_rc *rc, struct uwb_rc_evt_beacon *be, @@ -437,18 +436,18 @@ int uwbd_evt_handle_rc_beacon(struct uwb_event *evt) if (uwb_mac_addr_bcast(&bf->Device_Identifier)) return 0; - mutex_lock(&uwb_beca.mutex); - bce = __uwb_beca_find_bymac(&bf->Device_Identifier); + mutex_lock(&rc->uwb_beca.mutex); + bce = __uwb_beca_find_bymac(rc, &bf->Device_Identifier); if (bce == NULL) { /* Not in there, a new device is pinging */ uwb_beacon_print(evt->rc, be, bf); - bce = __uwb_beca_add(be, bf, evt->ts_jiffies); + bce = __uwb_beca_add(rc, be, bf, evt->ts_jiffies); if (bce == NULL) { - mutex_unlock(&uwb_beca.mutex); + mutex_unlock(&rc->uwb_beca.mutex); return -ENOMEM; } } - mutex_unlock(&uwb_beca.mutex); + mutex_unlock(&rc->uwb_beca.mutex); mutex_lock(&bce->mutex); /* purge old beacon data */ diff --git a/drivers/uwb/driver.c b/drivers/uwb/driver.c index 521cdeb84971..f57c26580de2 100644 --- a/drivers/uwb/driver.c +++ b/drivers/uwb/driver.c @@ -118,7 +118,6 @@ static int __init uwb_subsys_init(void) result = class_register(&uwb_rc_class); if (result < 0) goto error_uwb_rc_class_register; - uwbd_start(); uwb_dbg_init(); return 0; @@ -132,7 +131,6 @@ module_init(uwb_subsys_init); static void __exit uwb_subsys_exit(void) { uwb_dbg_exit(); - uwbd_stop(); class_unregister(&uwb_rc_class); uwb_est_destroy(); return; diff --git a/drivers/uwb/lc-rc.c b/drivers/uwb/lc-rc.c index 1129e8767b58..38e3d57ec8f7 100644 --- a/drivers/uwb/lc-rc.c +++ b/drivers/uwb/lc-rc.c @@ -36,8 +36,6 @@ #include #include -#define D_LOCAL 1 -#include #include "uwb-internal.h" static int uwb_rc_index_match(struct device *dev, void *data) @@ -83,7 +81,6 @@ static void uwb_rc_sys_release(struct device *dev) uwb_rc_neh_destroy(rc); uwb_rc_ie_release(rc); - d_printf(1, dev, "freed uwb_rc %p\n", rc); kfree(rc); } @@ -100,6 +97,8 @@ void uwb_rc_init(struct uwb_rc *rc) rc->scan_type = UWB_SCAN_DISABLED; INIT_LIST_HEAD(&rc->notifs_chain.list); mutex_init(&rc->notifs_chain.mutex); + INIT_LIST_HEAD(&rc->uwb_beca.list); + mutex_init(&rc->uwb_beca.mutex); uwb_drp_avail_init(rc); uwb_rc_ie_init(rc); uwb_rsv_init(rc); @@ -250,6 +249,12 @@ int uwb_rc_add(struct uwb_rc *rc, struct device *parent_dev, void *priv) rc->priv = priv; + init_waitqueue_head(&rc->uwbd.wq); + INIT_LIST_HEAD(&rc->uwbd.event_list); + spin_lock_init(&rc->uwbd.event_list_lock); + + uwbd_start(rc); + result = rc->start(rc); if (result < 0) goto error_rc_start; @@ -284,7 +289,7 @@ error_sys_add: error_dev_add: error_rc_setup: rc->stop(rc); - uwbd_flush(rc); + uwbd_stop(rc); error_rc_start: return result; } @@ -315,16 +320,18 @@ void uwb_rc_rm(struct uwb_rc *rc) uwb_rc_reset(rc); rc->stop(rc); - uwbd_flush(rc); + + uwbd_stop(rc); uwb_dev_lock(&rc->uwb_dev); rc->priv = NULL; rc->cmd = NULL; uwb_dev_unlock(&rc->uwb_dev); - mutex_lock(&uwb_beca.mutex); + mutex_lock(&rc->uwb_beca.mutex); uwb_dev_for_each(rc, uwb_dev_offair_helper, NULL); __uwb_rc_sys_rm(rc); - mutex_unlock(&uwb_beca.mutex); + mutex_unlock(&rc->uwb_beca.mutex); + uwb_beca_release(rc); uwb_dev_rm(&rc->uwb_dev); } EXPORT_SYMBOL_GPL(uwb_rc_rm); diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h index 031e8a885681..4c2449679978 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/uwb/uwb-internal.h @@ -160,8 +160,8 @@ struct uwb_event { }; }; -extern void uwbd_start(void); -extern void uwbd_stop(void); +extern void uwbd_start(struct uwb_rc *rc); +extern void uwbd_stop(struct uwb_rc *rc); extern struct uwb_event *uwb_event_alloc(size_t, gfp_t gfp_mask); extern void uwbd_event_queue(struct uwb_event *); void uwbd_flush(struct uwb_rc *rc); @@ -194,15 +194,6 @@ int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt); extern unsigned long beacon_timeout_ms; -/** Beacon cache list */ -struct uwb_beca { - struct list_head list; - size_t entries; - struct mutex mutex; -}; - -extern struct uwb_beca uwb_beca; - /** * Beacon cache entry * @@ -229,9 +220,6 @@ struct uwb_beca_e { struct uwb_beacon_frame; extern ssize_t uwb_bce_print_IEs(struct uwb_dev *, struct uwb_beca_e *, char *, size_t); -extern struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *, - struct uwb_beacon_frame *, - unsigned long); extern void uwb_bce_kfree(struct kref *_bce); static inline void uwb_bce_get(struct uwb_beca_e *bce) @@ -242,8 +230,8 @@ static inline void uwb_bce_put(struct uwb_beca_e *bce) { kref_put(&bce->refcnt, uwb_bce_kfree); } -extern void uwb_beca_purge(void); -extern void uwb_beca_release(void); +extern void uwb_beca_purge(struct uwb_rc *rc); +extern void uwb_beca_release(struct uwb_rc *rc); struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc, const struct uwb_dev_addr *devaddr); diff --git a/drivers/uwb/uwbd.c b/drivers/uwb/uwbd.c index f75113571f4a..ec42ce92dbce 100644 --- a/drivers/uwb/uwbd.c +++ b/drivers/uwb/uwbd.c @@ -170,8 +170,6 @@ static const struct uwbd_event uwbd_message_handlers[] = { }, }; -static DEFINE_MUTEX(uwbd_event_mutex); - /** * Handle an URC event passed to the UWB Daemon * @@ -235,19 +233,10 @@ static void uwbd_event_handle_message(struct uwb_event *evt) return; } - /* If this is a reset event we need to drop the - * uwbd_event_mutex or it deadlocks when the reset handler - * attempts to flush the uwbd events. */ - if (evt->message == UWB_EVT_MSG_RESET) - mutex_unlock(&uwbd_event_mutex); - result = uwbd_message_handlers[evt->message].handler(evt); if (result < 0) dev_err(&rc->uwb_dev.dev, "UWBD: '%s' message failed: %d\n", uwbd_message_handlers[evt->message].name, result); - - if (evt->message == UWB_EVT_MSG_RESET) - mutex_lock(&uwbd_event_mutex); } static void uwbd_event_handle(struct uwb_event *evt) @@ -275,20 +264,6 @@ static void uwbd_event_handle(struct uwb_event *evt) __uwb_rc_put(rc); /* for the __uwb_rc_get() in uwb_rc_notif_cb() */ } -/* The UWB Daemon */ - - -/** Daemon's PID: used to decide if we can queue or not */ -static int uwbd_pid; -/** Daemon's task struct for managing the kthread */ -static struct task_struct *uwbd_task; -/** Daemon's waitqueue for waiting for new events */ -static DECLARE_WAIT_QUEUE_HEAD(uwbd_wq); -/** Daemon's list of events; we queue/dequeue here */ -static struct list_head uwbd_event_list = LIST_HEAD_INIT(uwbd_event_list); -/** Daemon's list lock to protect concurent access */ -static DEFINE_SPINLOCK(uwbd_event_list_lock); - /** * UWB Daemon @@ -302,65 +277,58 @@ static DEFINE_SPINLOCK(uwbd_event_list_lock); * FIXME: should change so we don't have a 1HZ timer all the time, but * only if there are devices. */ -static int uwbd(void *unused) +static int uwbd(void *param) { + struct uwb_rc *rc = param; unsigned long flags; - struct list_head list = LIST_HEAD_INIT(list); - struct uwb_event *evt, *nxt; + struct uwb_event *evt; int should_stop = 0; + while (1) { wait_event_interruptible_timeout( - uwbd_wq, - !list_empty(&uwbd_event_list) + rc->uwbd.wq, + !list_empty(&rc->uwbd.event_list) || (should_stop = kthread_should_stop()), HZ); if (should_stop) break; try_to_freeze(); - mutex_lock(&uwbd_event_mutex); - spin_lock_irqsave(&uwbd_event_list_lock, flags); - list_splice_init(&uwbd_event_list, &list); - spin_unlock_irqrestore(&uwbd_event_list_lock, flags); - list_for_each_entry_safe(evt, nxt, &list, list_node) { + spin_lock_irqsave(&rc->uwbd.event_list_lock, flags); + if (!list_empty(&rc->uwbd.event_list)) { + evt = list_first_entry(&rc->uwbd.event_list, struct uwb_event, list_node); list_del(&evt->list_node); + } else + evt = NULL; + spin_unlock_irqrestore(&rc->uwbd.event_list_lock, flags); + + if (evt) { uwbd_event_handle(evt); kfree(evt); } - mutex_unlock(&uwbd_event_mutex); - uwb_beca_purge(); /* Purge devices that left */ + uwb_beca_purge(rc); /* Purge devices that left */ } return 0; } /** Start the UWB daemon */ -void uwbd_start(void) +void uwbd_start(struct uwb_rc *rc) { - uwbd_task = kthread_run(uwbd, NULL, "uwbd"); - if (uwbd_task == NULL) + rc->uwbd.task = kthread_run(uwbd, rc, "uwbd"); + if (rc->uwbd.task == NULL) printk(KERN_ERR "UWB: Cannot start management daemon; " "UWB won't work\n"); else - uwbd_pid = uwbd_task->pid; + rc->uwbd.pid = rc->uwbd.task->pid; } /* Stop the UWB daemon and free any unprocessed events */ -void uwbd_stop(void) +void uwbd_stop(struct uwb_rc *rc) { - unsigned long flags; - struct uwb_event *evt, *nxt; - kthread_stop(uwbd_task); - spin_lock_irqsave(&uwbd_event_list_lock, flags); - uwbd_pid = 0; - list_for_each_entry_safe(evt, nxt, &uwbd_event_list, list_node) { - if (evt->type == UWB_EVT_TYPE_NOTIF) - kfree(evt->notif.rceb); - kfree(evt); - } - spin_unlock_irqrestore(&uwbd_event_list_lock, flags); - uwb_beca_release(); + kthread_stop(rc->uwbd.task); + uwbd_flush(rc); } /* @@ -377,18 +345,20 @@ void uwbd_stop(void) */ void uwbd_event_queue(struct uwb_event *evt) { + struct uwb_rc *rc = evt->rc; unsigned long flags; - spin_lock_irqsave(&uwbd_event_list_lock, flags); - if (uwbd_pid != 0) { - list_add(&evt->list_node, &uwbd_event_list); - wake_up_all(&uwbd_wq); + + spin_lock_irqsave(&rc->uwbd.event_list_lock, flags); + if (rc->uwbd.pid != 0) { + list_add(&evt->list_node, &rc->uwbd.event_list); + wake_up_all(&rc->uwbd.wq); } else { __uwb_rc_put(evt->rc); if (evt->type == UWB_EVT_TYPE_NOTIF) kfree(evt->notif.rceb); kfree(evt); } - spin_unlock_irqrestore(&uwbd_event_list_lock, flags); + spin_unlock_irqrestore(&rc->uwbd.event_list_lock, flags); return; } @@ -396,10 +366,8 @@ void uwbd_flush(struct uwb_rc *rc) { struct uwb_event *evt, *nxt; - mutex_lock(&uwbd_event_mutex); - - spin_lock_irq(&uwbd_event_list_lock); - list_for_each_entry_safe(evt, nxt, &uwbd_event_list, list_node) { + spin_lock_irq(&rc->uwbd.event_list_lock); + list_for_each_entry_safe(evt, nxt, &rc->uwbd.event_list, list_node) { if (evt->rc == rc) { __uwb_rc_put(rc); list_del(&evt->list_node); @@ -408,7 +376,5 @@ void uwbd_flush(struct uwb_rc *rc) kfree(evt); } } - spin_unlock_irq(&uwbd_event_list_lock); - - mutex_unlock(&uwbd_event_mutex); + spin_unlock_irq(&rc->uwbd.event_list_lock); } diff --git a/include/linux/uwb.h b/include/linux/uwb.h index 6d93f54b8879..881f0c5b6d28 100644 --- a/include/linux/uwb.h +++ b/include/linux/uwb.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -86,6 +87,22 @@ struct uwb_notifs_chain { struct mutex mutex; }; +/* Beacon cache list */ +struct uwb_beca { + struct list_head list; + size_t entries; + struct mutex mutex; +}; + +/* Event handling thread. */ +struct uwbd { + int pid; + struct task_struct *task; + wait_queue_head_t wq; + struct list_head event_list; + spinlock_t event_list_lock; +}; + /** * struct uwb_mas_bm - a bitmap of all MAS in a superframe * @bm: a bitmap of length #UWB_NUM_MAS @@ -342,6 +359,9 @@ struct uwb_rc { enum uwb_scan_type scan_type:3; unsigned ready:1; struct uwb_notifs_chain notifs_chain; + struct uwb_beca uwb_beca; + + struct uwbd uwbd; struct uwb_drp_avail drp_avail; struct list_head reservations; -- cgit v1.2.3 From 307ba6dd73254fe7d2ce27db64ffd90e1bb3c6c0 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Fri, 7 Nov 2008 17:37:33 +0000 Subject: uwb: don't unbind the radio controller driver when resetting Use pre_reset and post_reset methods to avoid unbinding the radio controller driver after a uwb_rc_reset_all() call. This avoids a deadlock in uwb_rc_rm() when waiting for the uwb event thread to stop. Signed-off-by: David Vrabel --- drivers/uwb/hwa-rc.c | 22 +++++++++++++++- drivers/uwb/reset.c | 49 ++++++++++++++++++++++++++++++------ drivers/uwb/rsv.c | 29 +++++++++++++++------- drivers/uwb/umc-bus.c | 62 ++++++++++++++++++++++++++++++++-------------- drivers/uwb/uwb-internal.h | 1 + drivers/uwb/whc-rc.c | 30 ++++++++++++++++++---- include/linux/uwb.h | 2 ++ include/linux/uwb/umc.h | 2 ++ 8 files changed, 155 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c index 18009c99577d..158e98d08af9 100644 --- a/drivers/uwb/hwa-rc.c +++ b/drivers/uwb/hwa-rc.c @@ -881,6 +881,24 @@ static void hwarc_disconnect(struct usb_interface *iface) uwb_rc_put(uwb_rc); /* when creating the device, refcount = 1 */ } +static int hwarc_pre_reset(struct usb_interface *iface) +{ + struct hwarc *hwarc = usb_get_intfdata(iface); + struct uwb_rc *uwb_rc = hwarc->uwb_rc; + + uwb_rc_pre_reset(uwb_rc); + return 0; +} + +static int hwarc_post_reset(struct usb_interface *iface) +{ + struct hwarc *hwarc = usb_get_intfdata(iface); + struct uwb_rc *uwb_rc = hwarc->uwb_rc; + + uwb_rc_post_reset(uwb_rc); + return 0; +} + /** USB device ID's that we handle */ static struct usb_device_id hwarc_id_table[] = { /* D-Link DUB-1210 */ @@ -897,9 +915,11 @@ MODULE_DEVICE_TABLE(usb, hwarc_id_table); static struct usb_driver hwarc_driver = { .name = "hwa-rc", + .id_table = hwarc_id_table, .probe = hwarc_probe, .disconnect = hwarc_disconnect, - .id_table = hwarc_id_table, + .pre_reset = hwarc_pre_reset, + .post_reset = hwarc_post_reset, }; static int __init hwarc_driver_init(void) diff --git a/drivers/uwb/reset.c b/drivers/uwb/reset.c index 8de856fa7958..e39b32099af3 100644 --- a/drivers/uwb/reset.c +++ b/drivers/uwb/reset.c @@ -323,17 +323,16 @@ int uwbd_msg_handle_reset(struct uwb_event *evt) struct uwb_rc *rc = evt->rc; int ret; - /* Need to prevent the RC hardware module going away while in - the rc->reset() call. */ - if (!try_module_get(rc->owner)) - return 0; - dev_info(&rc->uwb_dev.dev, "resetting radio controller\n"); ret = rc->reset(rc); - if (ret) + if (ret) { dev_err(&rc->uwb_dev.dev, "failed to reset hardware: %d\n", ret); - - module_put(rc->owner); + goto error; + } + return 0; +error: + /* Nothing can be done except try the reset again. */ + uwb_rc_reset_all(rc); return ret; } @@ -360,3 +359,37 @@ void uwb_rc_reset_all(struct uwb_rc *rc) uwbd_event_queue(evt); } EXPORT_SYMBOL_GPL(uwb_rc_reset_all); + +void uwb_rc_pre_reset(struct uwb_rc *rc) +{ + rc->stop(rc); + uwbd_flush(rc); + + mutex_lock(&rc->uwb_dev.mutex); + rc->beaconing = -1; + rc->scanning = -1; + mutex_unlock(&rc->uwb_dev.mutex); + + uwb_rsv_remove_all(rc); +} +EXPORT_SYMBOL_GPL(uwb_rc_pre_reset); + +void uwb_rc_post_reset(struct uwb_rc *rc) +{ + int ret; + + ret = rc->start(rc); + if (ret) + goto error; + ret = uwb_rc_mac_addr_set(rc, &rc->uwb_dev.mac_addr); + if (ret) + goto error; + ret = uwb_rc_dev_addr_set(rc, &rc->uwb_dev.dev_addr); + if (ret) + goto error; + return; +error: + /* Nothing can be done except try the reset again. */ + uwb_rc_reset_all(rc); +} +EXPORT_SYMBOL_GPL(uwb_rc_post_reset); diff --git a/drivers/uwb/rsv.c b/drivers/uwb/rsv.c index ce0094657d3d..3d76efe26acc 100644 --- a/drivers/uwb/rsv.c +++ b/drivers/uwb/rsv.c @@ -659,6 +659,25 @@ static void uwb_rsv_timer(unsigned long arg) uwb_rsv_sched_update(rsv->rc); } +/** + * uwb_rsv_remove_all - remove all reservations + * @rc: the radio controller + * + * A DRP IE update is not done. + */ +void uwb_rsv_remove_all(struct uwb_rc *rc) +{ + struct uwb_rsv *rsv, *t; + + mutex_lock(&rc->rsvs_mutex); + list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) { + uwb_rsv_remove(rsv); + } + mutex_unlock(&rc->rsvs_mutex); + + cancel_work_sync(&rc->rsv_update_work); +} + void uwb_rsv_init(struct uwb_rc *rc) { INIT_LIST_HEAD(&rc->reservations); @@ -682,14 +701,6 @@ int uwb_rsv_setup(struct uwb_rc *rc) void uwb_rsv_cleanup(struct uwb_rc *rc) { - struct uwb_rsv *rsv, *t; - - mutex_lock(&rc->rsvs_mutex); - list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) { - uwb_rsv_remove(rsv); - } - mutex_unlock(&rc->rsvs_mutex); - - cancel_work_sync(&rc->rsv_update_work); + uwb_rsv_remove_all(rc); destroy_workqueue(rc->rsv_workq); } diff --git a/drivers/uwb/umc-bus.c b/drivers/uwb/umc-bus.c index 2d8d62d9f53e..5ad36164c13b 100644 --- a/drivers/uwb/umc-bus.c +++ b/drivers/uwb/umc-bus.c @@ -11,23 +11,48 @@ #include #include -static int umc_bus_unbind_helper(struct device *dev, void *data) +static int umc_bus_pre_reset_helper(struct device *dev, void *data) { - struct device *parent = data; + int ret = 0; - if (dev->parent == parent && dev->driver) - device_release_driver(dev); - return 0; + if (dev->driver) { + struct umc_dev *umc = to_umc_dev(dev); + struct umc_driver *umc_drv = to_umc_driver(dev->driver); + + if (umc_drv->pre_reset) + ret = umc_drv->pre_reset(umc); + else + device_release_driver(dev); + } + return ret; +} + +static int umc_bus_post_reset_helper(struct device *dev, void *data) +{ + int ret = 0; + + if (dev->driver) { + struct umc_dev *umc = to_umc_dev(dev); + struct umc_driver *umc_drv = to_umc_driver(dev->driver); + + if (umc_drv->post_reset) + ret = umc_drv->post_reset(umc); + } else + ret = device_attach(dev); + + return ret; } /** * umc_controller_reset - reset the whole UMC controller * @umc: the UMC device for the radio controller. * - * Drivers will be unbound from all UMC devices belonging to the - * controller and then the radio controller will be rebound. The - * radio controller is expected to do a full hardware reset when it is - * probed. + * Drivers or all capabilities of the controller will have their + * pre_reset methods called or be unbound from their device. Then all + * post_reset methods will be called or the drivers will be rebound. + * + * Radio controllers must provide pre_reset and post_reset methods and + * reset the hardware in their start method. * * If this is called while a probe() or remove() is in progress it * will return -EAGAIN and not perform the reset. @@ -35,14 +60,13 @@ static int umc_bus_unbind_helper(struct device *dev, void *data) int umc_controller_reset(struct umc_dev *umc) { struct device *parent = umc->dev.parent; - int ret; + int ret = 0; - if (down_trylock(&parent->sem)) + if(down_trylock(&parent->sem)) return -EAGAIN; - bus_for_each_dev(&umc_bus_type, NULL, parent, umc_bus_unbind_helper); - ret = device_attach(&umc->dev); - if (ret == 1) - ret = 0; + ret = device_for_each_child(parent, parent, umc_bus_pre_reset_helper); + if (ret >= 0) + device_for_each_child(parent, parent, umc_bus_post_reset_helper); up(&parent->sem); return ret; @@ -75,10 +99,10 @@ static int umc_bus_rescan_helper(struct device *dev, void *data) if (!dev->driver) ret = device_attach(dev); - return ret < 0 ? ret : 0; + return ret; } -static void umc_bus_rescan(void) +static void umc_bus_rescan(struct device *parent) { int err; @@ -86,7 +110,7 @@ static void umc_bus_rescan(void) * We can't use bus_rescan_devices() here as it deadlocks when * it tries to retake the dev->parent semaphore. */ - err = bus_for_each_dev(&umc_bus_type, NULL, NULL, umc_bus_rescan_helper); + err = device_for_each_child(parent, NULL, umc_bus_rescan_helper); if (err < 0) printk(KERN_WARNING "%s: rescan of bus failed: %d\n", KBUILD_MODNAME, err); @@ -120,7 +144,7 @@ static int umc_device_probe(struct device *dev) if (err) put_device(dev); else - umc_bus_rescan(); + umc_bus_rescan(dev->parent); return err; } diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h index 4c2449679978..af95541dabcd 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/uwb/uwb-internal.h @@ -248,6 +248,7 @@ extern struct device_attribute dev_attr_scan; void uwb_rsv_init(struct uwb_rc *rc); int uwb_rsv_setup(struct uwb_rc *rc); void uwb_rsv_cleanup(struct uwb_rc *rc); +void uwb_rsv_remove_all(struct uwb_rc *rc); void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state); void uwb_rsv_remove(struct uwb_rsv *rsv); diff --git a/drivers/uwb/whc-rc.c b/drivers/uwb/whc-rc.c index 6c454eadd307..e0d66938ccd8 100644 --- a/drivers/uwb/whc-rc.c +++ b/drivers/uwb/whc-rc.c @@ -394,7 +394,7 @@ void whcrc_stop_rc(struct uwb_rc *rc) le_writel(0, whcrc->rc_base + URCCMD); whci_wait_for(&umc_dev->dev, whcrc->rc_base + URCSTS, - URCSTS_HALTED, 0, 40, "URCSTS.HALTED"); + URCSTS_HALTED, URCSTS_HALTED, 100, "URCSTS.HALTED"); } static void whcrc_init(struct whcrc *whcrc) @@ -488,6 +488,24 @@ static void whcrc_remove(struct umc_dev *umc_dev) d_printf(1, &umc_dev->dev, "freed whcrc %p\n", whcrc); } +static int whcrc_pre_reset(struct umc_dev *umc) +{ + struct whcrc *whcrc = umc_get_drvdata(umc); + struct uwb_rc *uwb_rc = whcrc->uwb_rc; + + uwb_rc_pre_reset(uwb_rc); + return 0; +} + +static int whcrc_post_reset(struct umc_dev *umc) +{ + struct whcrc *whcrc = umc_get_drvdata(umc); + struct uwb_rc *uwb_rc = whcrc->uwb_rc; + + uwb_rc_post_reset(uwb_rc); + return 0; +} + /* PCI device ID's that we handle [so it gets loaded] */ static struct pci_device_id whcrc_id_table[] = { { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) }, @@ -496,10 +514,12 @@ static struct pci_device_id whcrc_id_table[] = { MODULE_DEVICE_TABLE(pci, whcrc_id_table); static struct umc_driver whcrc_driver = { - .name = "whc-rc", - .cap_id = UMC_CAP_ID_WHCI_RC, - .probe = whcrc_probe, - .remove = whcrc_remove, + .name = "whc-rc", + .cap_id = UMC_CAP_ID_WHCI_RC, + .probe = whcrc_probe, + .remove = whcrc_remove, + .pre_reset = whcrc_pre_reset, + .post_reset = whcrc_post_reset, }; static int __init whcrc_driver_init(void) diff --git a/include/linux/uwb.h b/include/linux/uwb.h index 881f0c5b6d28..c4854848999d 100644 --- a/include/linux/uwb.h +++ b/include/linux/uwb.h @@ -540,6 +540,8 @@ void uwb_rc_rm(struct uwb_rc *); void uwb_rc_neh_grok(struct uwb_rc *, void *, size_t); void uwb_rc_neh_error(struct uwb_rc *, int); void uwb_rc_reset_all(struct uwb_rc *rc); +void uwb_rc_pre_reset(struct uwb_rc *rc); +void uwb_rc_post_reset(struct uwb_rc *rc); /** * uwb_rsv_is_owner - is the owner of this reservation the RC? diff --git a/include/linux/uwb/umc.h b/include/linux/uwb/umc.h index 36a39e34f8d7..4b4fc0f43855 100644 --- a/include/linux/uwb/umc.h +++ b/include/linux/uwb/umc.h @@ -89,6 +89,8 @@ struct umc_driver { void (*remove)(struct umc_dev *); int (*suspend)(struct umc_dev *, pm_message_t state); int (*resume)(struct umc_dev *); + int (*pre_reset)(struct umc_dev *); + int (*post_reset)(struct umc_dev *); struct device_driver driver; }; -- cgit v1.2.3 From e17be2b2a95b43fe0d5878adf330701bb7a77115 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 17 Nov 2008 15:24:14 +0000 Subject: uwb: add pal parameter to new reservation callback The pal parameter allows PALs to retrieve their PAL-specific data structure. Signed-off-by: David Vrabel --- drivers/uwb/rsv.c | 2 +- drivers/uwb/uwb-debug.c | 10 +++++----- include/linux/uwb.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/uwb/rsv.c b/drivers/uwb/rsv.c index 3d76efe26acc..935d5b536db7 100644 --- a/drivers/uwb/rsv.c +++ b/drivers/uwb/rsv.c @@ -558,7 +558,7 @@ static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc, spin_lock(&rc->pal_lock); list_for_each_entry(pal, &rc->pals, node) { if (pal->new_rsv) - pal->new_rsv(rsv); + pal->new_rsv(pal, rsv); if (rsv->state == UWB_RSV_STATE_T_ACCEPTED) break; } diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c index 88e6ac713817..217ebaac128d 100644 --- a/drivers/uwb/uwb-debug.c +++ b/drivers/uwb/uwb-debug.c @@ -306,13 +306,13 @@ static struct file_operations drp_avail_fops = { .owner = THIS_MODULE, }; -static void uwb_dbg_new_rsv(struct uwb_rsv *rsv) +static void uwb_dbg_new_rsv(struct uwb_pal *pal, struct uwb_rsv *rsv) { - struct uwb_rc *rc = rsv->rc; + struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal); - if (rc->dbg->accept) { - list_add_tail(&rsv->pal_node, &rc->dbg->rsvs); - uwb_rsv_accept(rsv, uwb_dbg_rsv_cb, NULL); + if (dbg->accept) { + list_add_tail(&rsv->pal_node, &dbg->rsvs); + uwb_rsv_accept(rsv, uwb_dbg_rsv_cb, dbg); } } diff --git a/include/linux/uwb.h b/include/linux/uwb.h index c4854848999d..effd97998fd1 100644 --- a/include/linux/uwb.h +++ b/include/linux/uwb.h @@ -405,7 +405,7 @@ struct uwb_pal { struct list_head node; const char *name; struct device *device; - void (*new_rsv)(struct uwb_rsv *rsv); + void (*new_rsv)(struct uwb_pal *pal, struct uwb_rsv *rsv); }; void uwb_pal_init(struct uwb_pal *pal); -- cgit v1.2.3 From 6fae35f9cea92793a98b2d9ab21235e5ae035581 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 17 Nov 2008 15:53:42 +0000 Subject: uwb: add basic radio manager The UWB radio manager coordinates the use of the radio between the PALs that may be using it. PALs request use of the radio with uwb_radio_start() and the radio manager will start beaconing if its not already doing so. When the last PAL has called uwb_radio_stop() beaconing will be stopped. In the future, the radio manager will have a more sophisticated channel selection algorithm, probably following the Channel Selection Policy from the WiMedia Alliance when it is finalized. For now, channel 9 (BG1, TFC1) is selected. The user may override the channel selected by the radio manager and may force the radio to stop beaconing. The WUSB Host Controller PAL makes use of this and there are two new debug PAL commands that can be used for testing. Signed-off-by: David Vrabel --- Documentation/ABI/testing/sysfs-class-uwb_rc | 14 +- Documentation/usb/wusb-cbaf | 9 -- drivers/usb/host/hwa-hc.c | 1 - drivers/usb/host/whci/hcd.c | 2 - drivers/usb/wusbcore/devconnect.c | 5 +- drivers/usb/wusbcore/mmc.c | 75 ++-------- drivers/usb/wusbcore/pal.c | 16 ++- drivers/usb/wusbcore/wusbhc.h | 8 +- drivers/uwb/Makefile | 1 + drivers/uwb/beacon.c | 26 ++-- drivers/uwb/drp.c | 24 +--- drivers/uwb/lc-rc.c | 11 +- drivers/uwb/pal.c | 20 +-- drivers/uwb/radio.c | 202 +++++++++++++++++++++++++++ drivers/uwb/reset.c | 6 +- drivers/uwb/rsv.c | 4 +- drivers/uwb/uwb-debug.c | 26 +++- drivers/uwb/uwb-internal.h | 5 + drivers/uwb/wlp/wlp-lc.c | 5 +- include/linux/uwb.h | 23 ++- include/linux/uwb/debug-cmd.h | 2 + 21 files changed, 323 insertions(+), 162 deletions(-) create mode 100644 drivers/uwb/radio.c (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-class-uwb_rc b/Documentation/ABI/testing/sysfs-class-uwb_rc index a0d18dbeb7a9..6a5fd072849d 100644 --- a/Documentation/ABI/testing/sysfs-class-uwb_rc +++ b/Documentation/ABI/testing/sysfs-class-uwb_rc @@ -32,14 +32,16 @@ Contact: linux-usb@vger.kernel.org Description: Write: - [] + - to start beaconing on a specific channel, or stop - beaconing if is -1. Valid channels depends - on the radio controller's supported band groups. + to force a specific channel to be used when beaconing, + or, if is -1, to prohibit beaconing. If + is 0, then the default channel selection + algorithm will be used. Valid channels depends on the + radio controller's supported band groups. - may be used to try and join a specific - beacon group if more than one was found during a scan. + Reading returns the currently active channel, or -1 if + the radio controller is not beaconing. What: /sys/class/uwb_rc/uwbN/scan Date: July 2008 diff --git a/Documentation/usb/wusb-cbaf b/Documentation/usb/wusb-cbaf index 2e78b70f3adc..426ddaaef96f 100644 --- a/Documentation/usb/wusb-cbaf +++ b/Documentation/usb/wusb-cbaf @@ -80,12 +80,6 @@ case $1 in start) for dev in ${2:-$hdevs} do - uwb_rc=$(readlink -f $dev/uwb_rc) - if cat $uwb_rc/beacon | grep -q -- "-1" - then - echo 13 0 > $uwb_rc/beacon - echo I: started beaconing on ch 13 on $(basename $uwb_rc) >&2 - fi echo $host_CHID > $dev/wusb_chid echo I: started host $(basename $dev) >&2 done @@ -95,9 +89,6 @@ case $1 in do echo 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > $dev/wusb_chid echo I: stopped host $(basename $dev) >&2 - uwb_rc=$(readlink -f $dev/uwb_rc) - echo -1 | cat > $uwb_rc/beacon - echo I: stopped beaconing on $(basename $uwb_rc) >&2 done ;; set-chid) diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 2827353e97e1..2a4d36fa70b0 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -221,7 +221,6 @@ static void hwahc_op_stop(struct usb_hcd *usb_hcd) d_fnstart(4, dev, "(hwahc %p)\n", hwahc); mutex_lock(&wusbhc->mutex); - wusbhc_stop(wusbhc); wusb_cluster_id_put(wusbhc->cluster_id); mutex_unlock(&wusbhc->mutex); d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result); diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index de1e07271b81..f599f89d3be1 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c @@ -91,8 +91,6 @@ static void whc_stop(struct usb_hcd *usb_hcd) mutex_lock(&wusbhc->mutex); - wusbhc_stop(wusbhc); - /* stop HC */ le_writel(0, whc->base + WUSBINTR); whc_write_wusbcmd(whc, WUSBCMD_RUN, 0); diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index c01c7a80744c..08a1ec903867 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -1124,8 +1124,7 @@ void wusbhc_devconnect_destroy(struct wusbhc *wusbhc) * FIXME: This also enables the keep alives but this is not necessary * until there are connected and authenticated devices. */ -int wusbhc_devconnect_start(struct wusbhc *wusbhc, - const struct wusb_ckhdid *chid) +int wusbhc_devconnect_start(struct wusbhc *wusbhc) { struct device *dev = wusbhc->dev; struct wuie_host_info *hi; @@ -1138,7 +1137,7 @@ int wusbhc_devconnect_start(struct wusbhc *wusbhc, hi->hdr.bLength = sizeof(*hi); hi->hdr.bIEIdentifier = WUIE_ID_HOST_INFO; hi->attributes = cpu_to_le16((wusbhc->rsv->stream << 3) | WUIE_HI_CAP_ALL); - hi->CHID = *chid; + hi->CHID = wusbhc->chid; result = wusbhc_mmcie_set(wusbhc, 0, 0, &hi->hdr); if (result < 0) { dev_err(dev, "Cannot add Host Info MMCIE: %d\n", result); diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/usb/wusbcore/mmc.c index af2aee0fdffa..5463ecebafdf 100644 --- a/drivers/usb/wusbcore/mmc.c +++ b/drivers/usb/wusbcore/mmc.c @@ -162,12 +162,11 @@ EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm); /* * wusbhc_start - start transmitting MMCs and accepting connections * @wusbhc: the HC to start - * @chid: the CHID to use for this host * * Establishes a cluster reservation, enables device connections, and * starts MMCs with appropriate DNTS parameters. */ -int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) +int wusbhc_start(struct wusbhc *wusbhc) { int result; struct device *dev = wusbhc->dev; @@ -181,7 +180,7 @@ int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) goto error_rsv_establish; } - result = wusbhc_devconnect_start(wusbhc, chid); + result = wusbhc_devconnect_start(wusbhc); if (result < 0) { dev_err(dev, "error enabling device connections: %d\n", result); goto error_devconnect_start; @@ -218,34 +217,6 @@ error_rsv_establish: return result; } -/* - * Disconnect all from the WUSB Channel - * - * Send a Host Disconnect IE in the MMC, wait, don't send it any more - */ -static int __wusbhc_host_disconnect_ie(struct wusbhc *wusbhc) -{ - int result = -ENOMEM; - struct wuie_host_disconnect *host_disconnect_ie; - might_sleep(); - host_disconnect_ie = kmalloc(sizeof(*host_disconnect_ie), GFP_KERNEL); - if (host_disconnect_ie == NULL) - goto error_alloc; - host_disconnect_ie->hdr.bLength = sizeof(*host_disconnect_ie); - host_disconnect_ie->hdr.bIEIdentifier = WUIE_ID_HOST_DISCONNECT; - result = wusbhc_mmcie_set(wusbhc, 0, 0, &host_disconnect_ie->hdr); - if (result < 0) - goto error_mmcie_set; - - /* WUSB1.0[8.5.3.1 & 7.5.2] */ - msleep(100); - wusbhc_mmcie_rm(wusbhc, &host_disconnect_ie->hdr); -error_mmcie_set: - kfree(host_disconnect_ie); -error_alloc: - return result; -} - /* * wusbhc_stop - stop transmitting MMCs * @wusbhc: the HC to stop @@ -264,29 +235,6 @@ void wusbhc_stop(struct wusbhc *wusbhc) } EXPORT_SYMBOL_GPL(wusbhc_stop); -/* - * Change the CHID in a WUSB Channel - * - * If it is just a new CHID, send a Host Disconnect IE and then change - * the CHID IE. - */ -static int __wusbhc_chid_change(struct wusbhc *wusbhc, - const struct wusb_ckhdid *chid) -{ - int result = -ENOSYS; - struct device *dev = wusbhc->dev; - dev_err(dev, "%s() not implemented yet\n", __func__); - return result; - - BUG_ON(wusbhc->wuie_host_info == NULL); - __wusbhc_host_disconnect_ie(wusbhc); - wusbhc->wuie_host_info->CHID = *chid; - result = wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->wuie_host_info->hdr); - if (result < 0) - dev_err(dev, "Can't update Host Info WUSB IE: %d\n", result); - return result; -} - /* * Set/reset/update a new CHID * @@ -302,16 +250,19 @@ int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) chid = NULL; mutex_lock(&wusbhc->mutex); - if (wusbhc->active) { - if (chid) - result = __wusbhc_chid_change(wusbhc, chid); - else - wusbhc_stop(wusbhc); - } else { - if (chid) - wusbhc_start(wusbhc, chid); + if (chid) { + if (wusbhc->active) { + mutex_unlock(&wusbhc->mutex); + return -EBUSY; + } + wusbhc->chid = *chid; } mutex_unlock(&wusbhc->mutex); + + if (chid) + result = uwb_radio_start(&wusbhc->pal); + else + uwb_radio_stop(&wusbhc->pal); return result; } EXPORT_SYMBOL_GPL(wusbhc_chid_set); diff --git a/drivers/usb/wusbcore/pal.c b/drivers/usb/wusbcore/pal.c index 7cc51e9905cf..d0b172c5ecc7 100644 --- a/drivers/usb/wusbcore/pal.c +++ b/drivers/usb/wusbcore/pal.c @@ -18,6 +18,16 @@ */ #include "wusbhc.h" +static void wusbhc_channel_changed(struct uwb_pal *pal, int channel) +{ + struct wusbhc *wusbhc = container_of(pal, struct wusbhc, pal); + + if (channel < 0) + wusbhc_stop(wusbhc); + else + wusbhc_start(wusbhc); +} + /** * wusbhc_pal_register - register the WUSB HC as a UWB PAL * @wusbhc: the WUSB HC @@ -28,8 +38,10 @@ int wusbhc_pal_register(struct wusbhc *wusbhc) wusbhc->pal.name = "wusbhc"; wusbhc->pal.device = wusbhc->usb_hcd.self.controller; + wusbhc->pal.rc = wusbhc->uwb_rc; + wusbhc->pal.channel_changed = wusbhc_channel_changed; - return uwb_pal_register(wusbhc->uwb_rc, &wusbhc->pal); + return uwb_pal_register(&wusbhc->pal); } /** @@ -38,5 +50,5 @@ int wusbhc_pal_register(struct wusbhc *wusbhc) */ void wusbhc_pal_unregister(struct wusbhc *wusbhc) { - uwb_pal_unregister(wusbhc->uwb_rc, &wusbhc->pal); + uwb_pal_unregister(&wusbhc->pal); } diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h index 8fef934ad2f3..797c2453a35b 100644 --- a/drivers/usb/wusbcore/wusbhc.h +++ b/drivers/usb/wusbcore/wusbhc.h @@ -252,7 +252,8 @@ struct wusbhc { struct uwb_pal pal; unsigned trust_timeout; /* in jiffies */ - struct wuie_host_info *wuie_host_info; /* Includes CHID */ + struct wusb_ckhdid chid; + struct wuie_host_info *wuie_host_info; struct mutex mutex; /* locks everything else */ u16 cluster_id; /* Wireless USB Cluster ID */ @@ -376,15 +377,14 @@ static inline void wusbhc_put(struct wusbhc *wusbhc) usb_put_hcd(&wusbhc->usb_hcd); } -int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid); +int wusbhc_start(struct wusbhc *wusbhc); void wusbhc_stop(struct wusbhc *wusbhc); extern int wusbhc_chid_set(struct wusbhc *, const struct wusb_ckhdid *); /* Device connect handling */ extern int wusbhc_devconnect_create(struct wusbhc *); extern void wusbhc_devconnect_destroy(struct wusbhc *); -extern int wusbhc_devconnect_start(struct wusbhc *wusbhc, - const struct wusb_ckhdid *chid); +extern int wusbhc_devconnect_start(struct wusbhc *wusbhc); extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc); extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr, struct wusb_dn_hdr *dn_hdr, size_t size); diff --git a/drivers/uwb/Makefile b/drivers/uwb/Makefile index 2b99c3e61671..ce21a95da04a 100644 --- a/drivers/uwb/Makefile +++ b/drivers/uwb/Makefile @@ -18,6 +18,7 @@ uwb-objs := \ lc-rc.o \ neh.o \ pal.o \ + radio.o \ reset.o \ rsv.o \ scan.o \ diff --git a/drivers/uwb/beacon.c b/drivers/uwb/beacon.c index d9f2a8acc593..247956098afa 100644 --- a/drivers/uwb/beacon.c +++ b/drivers/uwb/beacon.c @@ -119,7 +119,6 @@ int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset) int result; struct device *dev = &rc->uwb_dev.dev; - mutex_lock(&rc->uwb_dev.mutex); if (channel < 0) channel = -1; if (channel == -1) @@ -128,7 +127,7 @@ int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset) /* channel >= 0...dah */ result = uwb_rc_start_beacon(rc, bpst_offset, channel); if (result < 0) - goto out_up; + return result; if (le16_to_cpu(rc->ies->wIELength) > 0) { result = uwb_rc_set_ie(rc, rc->ies); if (result < 0) { @@ -137,19 +136,14 @@ int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset) result = uwb_rc_stop_beacon(rc); channel = -1; bpst_offset = 0; - } else - result = 0; + } } } - if (result < 0) - goto out_up; - rc->beaconing = channel; - - uwb_notify(rc, NULL, uwb_bg_joined(rc) ? UWB_NOTIF_BG_JOIN : UWB_NOTIF_BG_LEAVE); - -out_up: - mutex_unlock(&rc->uwb_dev.mutex); + if (result >= 0) { + rc->beaconing = channel; + uwb_notify(rc, NULL, uwb_bg_joined(rc) ? UWB_NOTIF_BG_JOIN : UWB_NOTIF_BG_LEAVE); + } return result; } @@ -618,9 +612,6 @@ static ssize_t uwb_rc_beacon_show(struct device *dev, /* * Start beaconing on the specified channel, or stop beaconing. - * - * The BPST offset of when to start searching for a beacon group to - * join may be specified. */ static ssize_t uwb_rc_beacon_store(struct device *dev, struct device_attribute *attr, @@ -629,12 +620,11 @@ static ssize_t uwb_rc_beacon_store(struct device *dev, struct uwb_dev *uwb_dev = to_uwb_dev(dev); struct uwb_rc *rc = uwb_dev->rc; int channel; - unsigned bpst_offset = 0; ssize_t result = -EINVAL; - result = sscanf(buf, "%d %u\n", &channel, &bpst_offset); + result = sscanf(buf, "%d", &channel); if (result >= 1) - result = uwb_rc_beacon(rc, channel, bpst_offset); + result = uwb_radio_force_channel(rc, channel); return result < 0 ? result : size; } diff --git a/drivers/uwb/drp.c b/drivers/uwb/drp.c index c0b1e5e2bd08..fe328146adb7 100644 --- a/drivers/uwb/drp.c +++ b/drivers/uwb/drp.c @@ -37,14 +37,13 @@ * * A DRP Availability IE is appended. * - * rc->uwb_dev.mutex is held + * rc->rsvs_mutex is held * * FIXME We currently ignore the returned value indicating the remaining space * in beacon. This could be used to deny reservation requests earlier if * determined that they would cause the beacon space to be exceeded. */ -static -int uwb_rc_gen_send_drp_ie(struct uwb_rc *rc) +int uwb_rc_send_all_drp_ie(struct uwb_rc *rc) { int result; struct device *dev = &rc->uwb_dev.dev; @@ -102,25 +101,6 @@ error_cmd: kfree(cmd); error: return result; - -} -/** - * Send all DRP IEs associated with this host - * - * @returns: >= 0 number of bytes still available in the beacon - * < 0 errno code on error. - * - * As per the protocol we obtain the host controller device lock to access - * bandwidth structures. - */ -int uwb_rc_send_all_drp_ie(struct uwb_rc *rc) -{ - int result; - - mutex_lock(&rc->uwb_dev.mutex); - result = uwb_rc_gen_send_drp_ie(rc); - mutex_unlock(&rc->uwb_dev.mutex); - return result; } void uwb_drp_handle_timeout(struct uwb_rsv *rsv) diff --git a/drivers/uwb/lc-rc.c b/drivers/uwb/lc-rc.c index f00633d334dd..9cf21e6bb624 100644 --- a/drivers/uwb/lc-rc.c +++ b/drivers/uwb/lc-rc.c @@ -189,9 +189,9 @@ static int uwb_rc_setup(struct uwb_rc *rc) int result; struct device *dev = &rc->uwb_dev.dev; - result = uwb_rc_reset(rc); + result = uwb_radio_setup(rc); if (result < 0) { - dev_err(dev, "cannot reset UWB radio: %d\n", result); + dev_err(dev, "cannot setup UWB radio: %d\n", result); goto error; } result = uwb_rc_mac_addr_setup(rc); @@ -311,12 +311,7 @@ void uwb_rc_rm(struct uwb_rc *rc) uwb_dbg_del_rc(rc); uwb_rsv_remove_all(rc); - uwb_rc_ie_rm(rc, UWB_IDENTIFICATION_IE); - if (rc->beaconing >= 0) - uwb_rc_beacon(rc, -1, 0); - if (rc->scan_type != UWB_SCAN_DISABLED) - uwb_rc_scan(rc, rc->scanning, UWB_SCAN_DISABLED, 0); - uwb_rc_reset(rc); + uwb_radio_shutdown(rc); rc->stop(rc); diff --git a/drivers/uwb/pal.c b/drivers/uwb/pal.c index 1afb38eacb9a..605765124f5b 100644 --- a/drivers/uwb/pal.c +++ b/drivers/uwb/pal.c @@ -32,13 +32,13 @@ EXPORT_SYMBOL_GPL(uwb_pal_init); /** * uwb_pal_register - register a UWB PAL - * @rc: the radio controller the PAL will be using * @pal: the PAL * * The PAL must be initialized with uwb_pal_init(). */ -int uwb_pal_register(struct uwb_rc *rc, struct uwb_pal *pal) +int uwb_pal_register(struct uwb_pal *pal) { + struct uwb_rc *rc = pal->rc; int ret; if (pal->device) { @@ -54,9 +54,9 @@ int uwb_pal_register(struct uwb_rc *rc, struct uwb_pal *pal) } } - spin_lock(&rc->pal_lock); + mutex_lock(&rc->uwb_dev.mutex); list_add(&pal->node, &rc->pals); - spin_unlock(&rc->pal_lock); + mutex_unlock(&rc->uwb_dev.mutex); return 0; } @@ -64,14 +64,17 @@ EXPORT_SYMBOL_GPL(uwb_pal_register); /** * uwb_pal_register - unregister a UWB PAL - * @rc: the radio controller the PAL was using * @pal: the PAL */ -void uwb_pal_unregister(struct uwb_rc *rc, struct uwb_pal *pal) +void uwb_pal_unregister(struct uwb_pal *pal) { - spin_lock(&rc->pal_lock); + struct uwb_rc *rc = pal->rc; + + uwb_radio_stop(pal); + + mutex_lock(&rc->uwb_dev.mutex); list_del(&pal->node); - spin_unlock(&rc->pal_lock); + mutex_unlock(&rc->uwb_dev.mutex); if (pal->device) { sysfs_remove_link(&rc->uwb_dev.dev.kobj, pal->name); @@ -86,6 +89,5 @@ EXPORT_SYMBOL_GPL(uwb_pal_unregister); */ void uwb_rc_pal_init(struct uwb_rc *rc) { - spin_lock_init(&rc->pal_lock); INIT_LIST_HEAD(&rc->pals); } diff --git a/drivers/uwb/radio.c b/drivers/uwb/radio.c new file mode 100644 index 000000000000..f0d55495f5e9 --- /dev/null +++ b/drivers/uwb/radio.c @@ -0,0 +1,202 @@ +/* + * UWB radio (channel) management. + * + * Copyright (C) 2008 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include + +#include "uwb-internal.h" + + +static int uwb_radio_select_channel(struct uwb_rc *rc) +{ + /* + * Default to channel 9 (BG1, TFC1) unless the user has + * selected a specific channel or there are no active PALs. + */ + if (rc->active_pals == 0) + return -1; + if (rc->beaconing_forced) + return rc->beaconing_forced; + return 9; +} + + +/* + * Notify all active PALs that the channel has changed. + */ +static void uwb_radio_channel_changed(struct uwb_rc *rc, int channel) +{ + struct uwb_pal *pal; + + list_for_each_entry(pal, &rc->pals, node) { + if (pal->channel && channel != pal->channel) { + pal->channel = channel; + if (pal->channel_changed) + pal->channel_changed(pal, pal->channel); + } + } +} + +/* + * Change to a new channel and notify any active PALs of the new + * channel. + * + * When stopping the radio, PALs need to be notified first so they can + * terminate any active reservations. + */ +static int uwb_radio_change_channel(struct uwb_rc *rc, int channel) +{ + int ret = 0; + + if (channel == -1) + uwb_radio_channel_changed(rc, channel); + + if (channel != rc->beaconing) { + if (rc->beaconing != -1 && channel != -1) { + /* + * FIXME: should signal the channel change + * with a Channel Change IE. + */ + ret = uwb_radio_change_channel(rc, -1); + if (ret < 0) + return ret; + } + ret = uwb_rc_beacon(rc, channel, 0); + } + + if (channel != -1) + uwb_radio_channel_changed(rc, rc->beaconing); + + return ret; +} + +/** + * uwb_radio_start - request that the radio be started + * @pal: the PAL making the request. + * + * If the radio is not already active, aa suitable channel is selected + * and beacons are started. + */ +int uwb_radio_start(struct uwb_pal *pal) +{ + struct uwb_rc *rc = pal->rc; + int ret = 0; + + mutex_lock(&rc->uwb_dev.mutex); + + if (!pal->channel) { + pal->channel = -1; + rc->active_pals++; + ret = uwb_radio_change_channel(rc, uwb_radio_select_channel(rc)); + } + + mutex_unlock(&rc->uwb_dev.mutex); + return ret; +} +EXPORT_SYMBOL_GPL(uwb_radio_start); + +/** + * uwb_radio_stop - request tha the radio be stopped. + * @pal: the PAL making the request. + * + * Stops the radio if no other PAL is making use of it. + */ +void uwb_radio_stop(struct uwb_pal *pal) +{ + struct uwb_rc *rc = pal->rc; + + mutex_lock(&rc->uwb_dev.mutex); + + if (pal->channel) { + rc->active_pals--; + uwb_radio_change_channel(rc, uwb_radio_select_channel(rc)); + pal->channel = 0; + } + + mutex_unlock(&rc->uwb_dev.mutex); +} +EXPORT_SYMBOL_GPL(uwb_radio_stop); + +/* + * uwb_radio_force_channel - force a specific channel to be used + * @rc: the radio controller. + * @channel: the channel to use; -1 to force the radio to stop; 0 to + * use the default channel selection algorithm. + */ +int uwb_radio_force_channel(struct uwb_rc *rc, int channel) +{ + int ret = 0; + + mutex_lock(&rc->uwb_dev.mutex); + + rc->beaconing_forced = channel; + ret = uwb_radio_change_channel(rc, uwb_radio_select_channel(rc)); + + mutex_unlock(&rc->uwb_dev.mutex); + return ret; +} + +/* + * uwb_radio_setup - setup the radio manager + * @rc: the radio controller. + * + * The radio controller is reset to ensure it's in a known state + * before it's used. + */ +int uwb_radio_setup(struct uwb_rc *rc) +{ + return uwb_rc_reset(rc); +} + +/* + * uwb_radio_reset_state - reset any radio manager state + * @rc: the radio controller. + * + * All internal radio manager state is reset to values corresponding + * to a reset radio controller. + */ +void uwb_radio_reset_state(struct uwb_rc *rc) +{ + struct uwb_pal *pal; + + mutex_lock(&rc->uwb_dev.mutex); + + list_for_each_entry(pal, &rc->pals, node) { + if (pal->channel) { + pal->channel = -1; + if (pal->channel_changed) + pal->channel_changed(pal, -1); + } + } + + rc->beaconing = -1; + rc->scanning = -1; + + mutex_unlock(&rc->uwb_dev.mutex); +} + +/* + * uwb_radio_shutdown - shutdown the radio manager + * @rc: the radio controller. + * + * The radio controller is reset. + */ +void uwb_radio_shutdown(struct uwb_rc *rc) +{ + uwb_radio_reset_state(rc); + uwb_rc_reset(rc); +} diff --git a/drivers/uwb/reset.c b/drivers/uwb/reset.c index e39b32099af3..ce8283cc8098 100644 --- a/drivers/uwb/reset.c +++ b/drivers/uwb/reset.c @@ -365,11 +365,7 @@ void uwb_rc_pre_reset(struct uwb_rc *rc) rc->stop(rc); uwbd_flush(rc); - mutex_lock(&rc->uwb_dev.mutex); - rc->beaconing = -1; - rc->scanning = -1; - mutex_unlock(&rc->uwb_dev.mutex); - + uwb_radio_reset_state(rc); uwb_rsv_remove_all(rc); } EXPORT_SYMBOL_GPL(uwb_rc_pre_reset); diff --git a/drivers/uwb/rsv.c b/drivers/uwb/rsv.c index 935d5b536db7..1cd84f927540 100644 --- a/drivers/uwb/rsv.c +++ b/drivers/uwb/rsv.c @@ -555,14 +555,14 @@ static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc, * deny the request. */ rsv->state = UWB_RSV_STATE_T_DENIED; - spin_lock(&rc->pal_lock); + mutex_lock(&rc->uwb_dev.mutex); list_for_each_entry(pal, &rc->pals, node) { if (pal->new_rsv) pal->new_rsv(pal, rsv); if (rsv->state == UWB_RSV_STATE_T_ACCEPTED) break; } - spin_unlock(&rc->pal_lock); + mutex_unlock(&rc->uwb_dev.mutex); list_add_tail(&rsv->rc_node, &rc->reservations); state = rsv->state; diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c index 217ebaac128d..0e58071a232d 100644 --- a/drivers/uwb/uwb-debug.c +++ b/drivers/uwb/uwb-debug.c @@ -192,7 +192,7 @@ static ssize_t command_write(struct file *file, const char __user *buf, { struct uwb_rc *rc = file->private_data; struct uwb_dbg_cmd cmd; - int ret; + int ret = 0; if (len != sizeof(struct uwb_dbg_cmd)) return -EINVAL; @@ -213,6 +213,12 @@ static ssize_t command_write(struct file *file, const char __user *buf, case UWB_DBG_CMD_IE_RM: ret = cmd_ie_rm(rc, &cmd.ie_rm); break; + case UWB_DBG_CMD_RADIO_START: + ret = uwb_radio_start(&rc->dbg->pal); + break; + case UWB_DBG_CMD_RADIO_STOP: + uwb_radio_stop(&rc->dbg->pal); + break; default: return -EINVAL; } @@ -306,6 +312,17 @@ static struct file_operations drp_avail_fops = { .owner = THIS_MODULE, }; +static void uwb_dbg_channel_changed(struct uwb_pal *pal, int channel) +{ + struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal); + struct device *dev = &pal->rc->uwb_dev.dev; + + if (channel > 0) + dev_info(dev, "debug: channel %d started\n", channel); + else + dev_info(dev, "debug: channel stopped\n"); +} + static void uwb_dbg_new_rsv(struct uwb_pal *pal, struct uwb_rsv *rsv) { struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal); @@ -329,8 +346,11 @@ void uwb_dbg_add_rc(struct uwb_rc *rc) INIT_LIST_HEAD(&rc->dbg->rsvs); uwb_pal_init(&rc->dbg->pal); + rc->dbg->pal.rc = rc; + rc->dbg->pal.channel_changed = uwb_dbg_channel_changed; rc->dbg->pal.new_rsv = uwb_dbg_new_rsv; - uwb_pal_register(rc, &rc->dbg->pal); + uwb_pal_register(&rc->dbg->pal); + if (root_dir) { rc->dbg->root_d = debugfs_create_dir(dev_name(&rc->uwb_dev.dev), root_dir); @@ -364,7 +384,7 @@ void uwb_dbg_del_rc(struct uwb_rc *rc) uwb_rsv_terminate(rsv); } - uwb_pal_unregister(rc, &rc->dbg->pal); + uwb_pal_unregister(&rc->dbg->pal); if (root_dir) { debugfs_remove(rc->dbg->drp_avail_f); diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h index af95541dabcd..9c0cdb4ded0c 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/uwb/uwb-internal.h @@ -238,6 +238,11 @@ struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc, struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc, const struct uwb_mac_addr *macaddr); +int uwb_radio_setup(struct uwb_rc *rc); +void uwb_radio_reset_state(struct uwb_rc *rc); +void uwb_radio_shutdown(struct uwb_rc *rc); +int uwb_radio_force_channel(struct uwb_rc *rc, int channel); + /* -- UWB Sysfs representation */ extern struct class uwb_rc_class; extern struct device_attribute dev_attr_mac_address; diff --git a/drivers/uwb/wlp/wlp-lc.c b/drivers/uwb/wlp/wlp-lc.c index 0799402e73fb..7e5eb49b03b8 100644 --- a/drivers/uwb/wlp/wlp-lc.c +++ b/drivers/uwb/wlp/wlp-lc.c @@ -543,7 +543,8 @@ int wlp_setup(struct wlp *wlp, struct uwb_rc *rc) uwb_notifs_register(rc, &wlp->uwb_notifs_handler); uwb_pal_init(&wlp->pal); - result = uwb_pal_register(rc, &wlp->pal); + wlp->pal.rc = rc; + result = uwb_pal_register(&wlp->pal); if (result < 0) uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); @@ -557,7 +558,7 @@ void wlp_remove(struct wlp *wlp) struct device *dev = &wlp->rc->uwb_dev.dev; d_fnstart(6, dev, "wlp %p\n", wlp); wlp_neighbors_release(wlp); - uwb_pal_unregister(wlp->rc, &wlp->pal); + uwb_pal_unregister(&wlp->pal); uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); wlp_eda_release(&wlp->eda); mutex_lock(&wlp->mutex); diff --git a/include/linux/uwb.h b/include/linux/uwb.h index effd97998fd1..7d3ebf046f9a 100644 --- a/include/linux/uwb.h +++ b/include/linux/uwb.h @@ -355,6 +355,7 @@ struct uwb_rc { u8 ctx_roll; int beaconing; /* Beaconing state [channel number] */ + int beaconing_forced; int scanning; enum uwb_scan_type scan_type:3; unsigned ready:1; @@ -373,8 +374,8 @@ struct uwb_rc { struct uwb_rc_cmd_set_ie *ies; size_t ies_capacity; - spinlock_t pal_lock; struct list_head pals; + int active_pals; struct uwb_dbg *dbg; }; @@ -382,11 +383,17 @@ struct uwb_rc { /** * struct uwb_pal - a UWB PAL - * @name: descriptive name for this PAL (wushc, wlp, etc.). + * @name: descriptive name for this PAL (wusbhc, wlp, etc.). * @device: a device for the PAL. Used to link the PAL and the radio * controller in sysfs. + * @rc: the radio controller the PAL uses. + * @channel_changed: called when the channel used by the radio changes. + * A channel of -1 means the channel has been stopped. * @new_rsv: called when a peer requests a reservation (may be NULL if * the PAL cannot accept reservation requests). + * @channel: channel being used by the PAL; 0 if the PAL isn't using + * the radio; -1 if the PAL wishes to use the radio but + * cannot. * * A Protocol Adaptation Layer (PAL) is a user of the WiMedia UWB * radio platform (e.g., WUSB, WLP or Bluetooth UWB AMP). @@ -405,12 +412,20 @@ struct uwb_pal { struct list_head node; const char *name; struct device *device; + struct uwb_rc *rc; + + void (*channel_changed)(struct uwb_pal *pal, int channel); void (*new_rsv)(struct uwb_pal *pal, struct uwb_rsv *rsv); + + int channel; }; void uwb_pal_init(struct uwb_pal *pal); -int uwb_pal_register(struct uwb_rc *rc, struct uwb_pal *pal); -void uwb_pal_unregister(struct uwb_rc *rc, struct uwb_pal *pal); +int uwb_pal_register(struct uwb_pal *pal); +void uwb_pal_unregister(struct uwb_pal *pal); + +int uwb_radio_start(struct uwb_pal *pal); +void uwb_radio_stop(struct uwb_pal *pal); /* * General public API diff --git a/include/linux/uwb/debug-cmd.h b/include/linux/uwb/debug-cmd.h index 6a16566f0221..07efbe17db53 100644 --- a/include/linux/uwb/debug-cmd.h +++ b/include/linux/uwb/debug-cmd.h @@ -34,6 +34,8 @@ enum uwb_dbg_cmd_type { UWB_DBG_CMD_RSV_TERMINATE = 2, UWB_DBG_CMD_IE_ADD = 3, UWB_DBG_CMD_IE_RM = 4, + UWB_DBG_CMD_RADIO_START = 5, + UWB_DBG_CMD_RADIO_STOP = 6, }; struct uwb_dbg_cmd_rsv_establish { -- cgit v1.2.3 From e8e1594c8126b1b773988fa2e3bfec76cff88336 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 17 Nov 2008 16:16:51 +0000 Subject: wlp: start/stop radio on network interface up/down Signed-off-by: David Vrabel --- drivers/uwb/i1480/i1480u-wlp/lc.c | 2 +- drivers/uwb/i1480/i1480u-wlp/netdev.c | 51 ++++++++--------------------------- drivers/uwb/uwb-debug.c | 3 --- drivers/uwb/wlp/wlp-lc.c | 14 +++++++++- include/linux/wlp.h | 3 ++- 5 files changed, 27 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/drivers/uwb/i1480/i1480u-wlp/lc.c b/drivers/uwb/i1480/i1480u-wlp/lc.c index 384306c11bbe..488b2e30a0a8 100644 --- a/drivers/uwb/i1480/i1480u-wlp/lc.c +++ b/drivers/uwb/i1480/i1480u-wlp/lc.c @@ -206,7 +206,7 @@ int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface) wlp->fill_device_info = i1480u_fill_device_info; wlp->stop_queue = i1480u_stop_queue; wlp->start_queue = i1480u_start_queue; - result = wlp_setup(wlp, rc); + result = wlp_setup(wlp, rc, net_dev); if (result < 0) { dev_err(&iface->dev, "Cannot setup WLP\n"); goto error_wlp_setup; diff --git a/drivers/uwb/i1480/i1480u-wlp/netdev.c b/drivers/uwb/i1480/i1480u-wlp/netdev.c index 8802ac43d872..2eafb973cd05 100644 --- a/drivers/uwb/i1480/i1480u-wlp/netdev.c +++ b/drivers/uwb/i1480/i1480u-wlp/netdev.c @@ -207,6 +207,11 @@ int i1480u_open(struct net_device *net_dev) result = i1480u_rx_setup(i1480u); /* Alloc RX stuff */ if (result < 0) goto error_rx_setup; + + result = uwb_radio_start(&wlp->pal); + if (result < 0) + goto error_radio_start; + netif_wake_queue(net_dev); #ifdef i1480u_FLOW_CONTROL result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL);; @@ -215,25 +220,20 @@ int i1480u_open(struct net_device *net_dev) goto error_notif_urb_submit; } #endif - i1480u->uwb_notifs_handler.cb = i1480u_uwb_notifs_cb; - i1480u->uwb_notifs_handler.data = i1480u; - if (uwb_bg_joined(rc)) - netif_carrier_on(net_dev); - else - netif_carrier_off(net_dev); - uwb_notifs_register(rc, &i1480u->uwb_notifs_handler); /* Interface is up with an address, now we can create WSS */ result = wlp_wss_setup(net_dev, &wlp->wss); if (result < 0) { dev_err(dev, "Can't create WSS: %d. \n", result); - goto error_notif_deregister; + goto error_wss_setup; } return 0; -error_notif_deregister: - uwb_notifs_deregister(rc, &i1480u->uwb_notifs_handler); +error_wss_setup: #ifdef i1480u_FLOW_CONTROL + usb_kill_urb(i1480u->notif_urb); error_notif_urb_submit: #endif + uwb_radio_stop(&wlp->pal); +error_radio_start: netif_stop_queue(net_dev); i1480u_rx_release(i1480u); error_rx_setup: @@ -248,16 +248,15 @@ int i1480u_stop(struct net_device *net_dev) { struct i1480u *i1480u = netdev_priv(net_dev); struct wlp *wlp = &i1480u->wlp; - struct uwb_rc *rc = wlp->rc; BUG_ON(wlp->rc == NULL); wlp_wss_remove(&wlp->wss); - uwb_notifs_deregister(rc, &i1480u->uwb_notifs_handler); netif_carrier_off(net_dev); #ifdef i1480u_FLOW_CONTROL usb_kill_urb(i1480u->notif_urb); #endif netif_stop_queue(net_dev); + uwb_radio_stop(&wlp->pal); i1480u_rx_release(i1480u); i1480u_tx_release(i1480u); return 0; @@ -303,34 +302,6 @@ int i1480u_change_mtu(struct net_device *net_dev, int mtu) return 0; } - -/** - * Callback function to handle events from UWB - * When we see other devices we know the carrier is ok, - * if we are the only device in the beacon group we set the carrier - * state to off. - * */ -void i1480u_uwb_notifs_cb(void *data, struct uwb_dev *uwb_dev, - enum uwb_notifs event) -{ - struct i1480u *i1480u = data; - struct net_device *net_dev = i1480u->net_dev; - struct device *dev = &i1480u->usb_iface->dev; - switch (event) { - case UWB_NOTIF_BG_JOIN: - netif_carrier_on(net_dev); - dev_info(dev, "Link is up\n"); - break; - case UWB_NOTIF_BG_LEAVE: - netif_carrier_off(net_dev); - dev_info(dev, "Link is down\n"); - break; - default: - dev_err(dev, "don't know how to handle event %d from uwb\n", - event); - } -} - /** * Stop the network queue * diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c index 0e58071a232d..e02fb83469d5 100644 --- a/drivers/uwb/uwb-debug.c +++ b/drivers/uwb/uwb-debug.c @@ -33,8 +33,6 @@ #include #include -#define D_LOCAL 0 -#include #include "uwb-internal.h" @@ -314,7 +312,6 @@ static struct file_operations drp_avail_fops = { static void uwb_dbg_channel_changed(struct uwb_pal *pal, int channel) { - struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal); struct device *dev = &pal->rc->uwb_dev.dev; if (channel > 0) diff --git a/drivers/uwb/wlp/wlp-lc.c b/drivers/uwb/wlp/wlp-lc.c index 7e5eb49b03b8..e531093c4162 100644 --- a/drivers/uwb/wlp/wlp-lc.c +++ b/drivers/uwb/wlp/wlp-lc.c @@ -526,7 +526,17 @@ void wlp_uwb_notifs_cb(void *_wlp, struct uwb_dev *uwb_dev, } } -int wlp_setup(struct wlp *wlp, struct uwb_rc *rc) +static void wlp_channel_changed(struct uwb_pal *pal, int channel) +{ + struct wlp *wlp = container_of(pal, struct wlp, pal); + + if (channel < 0) + netif_carrier_off(wlp->ndev); + else + netif_carrier_on(wlp->ndev); +} + +int wlp_setup(struct wlp *wlp, struct uwb_rc *rc, struct net_device *ndev) { struct device *dev = &rc->uwb_dev.dev; int result; @@ -537,6 +547,7 @@ int wlp_setup(struct wlp *wlp, struct uwb_rc *rc) BUG_ON(wlp->stop_queue == NULL); BUG_ON(wlp->start_queue == NULL); wlp->rc = rc; + wlp->ndev = ndev; wlp_eda_init(&wlp->eda);/* Set up address cache */ wlp->uwb_notifs_handler.cb = wlp_uwb_notifs_cb; wlp->uwb_notifs_handler.data = wlp; @@ -544,6 +555,7 @@ int wlp_setup(struct wlp *wlp, struct uwb_rc *rc) uwb_pal_init(&wlp->pal); wlp->pal.rc = rc; + wlp->pal.channel_changed = wlp_channel_changed; result = uwb_pal_register(&wlp->pal); if (result < 0) uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); diff --git a/include/linux/wlp.h b/include/linux/wlp.h index 033545e145c7..ac95ce6606ac 100644 --- a/include/linux/wlp.h +++ b/include/linux/wlp.h @@ -646,6 +646,7 @@ struct wlp_wss { struct wlp { struct mutex mutex; struct uwb_rc *rc; /* UWB radio controller */ + struct net_device *ndev; struct uwb_pal pal; struct wlp_eda eda; struct wlp_uuid uuid; @@ -675,7 +676,7 @@ struct wlp_wss_attribute { static struct wlp_wss_attribute wss_attr_##_name = __ATTR(_name, _mode, \ _show, _store) -extern int wlp_setup(struct wlp *, struct uwb_rc *); +extern int wlp_setup(struct wlp *, struct uwb_rc *, struct net_device *ndev); extern void wlp_remove(struct wlp *); extern ssize_t wlp_neighborhood_show(struct wlp *, char *); extern int wlp_wss_setup(struct net_device *, struct wlp_wss *); -- cgit v1.2.3 From 0996e6382482ce9014787693d3884e9468153a5c Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 17 Nov 2008 16:23:22 +0000 Subject: uwb: remove unused beacon group join/leave events The UWB_NOTIF_BG_JOIN/UWB_NOTIF_BG_LEAVE events have been superceeded by the channel_changed callback in struct uwb_pal. Signed-off-by: David Vrabel --- drivers/uwb/beacon.c | 17 +---------------- include/linux/uwb.h | 7 +++---- 2 files changed, 4 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/uwb/beacon.c b/drivers/uwb/beacon.c index 247956098afa..d9c60cb94993 100644 --- a/drivers/uwb/beacon.c +++ b/drivers/uwb/beacon.c @@ -140,10 +140,8 @@ int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset) } } - if (result >= 0) { + if (result >= 0) rc->beaconing = channel; - uwb_notify(rc, NULL, uwb_bg_joined(rc) ? UWB_NOTIF_BG_JOIN : UWB_NOTIF_BG_LEAVE); - } return result; } @@ -581,19 +579,6 @@ error: return result; } -/** - * uwb_bg_joined - is the RC in a beacon group? - * @rc: the radio controller - * - * Returns true if the radio controller is in a beacon group (even if - * it's the sole member). - */ -int uwb_bg_joined(struct uwb_rc *rc) -{ - return rc->beaconing != -1; -} -EXPORT_SYMBOL_GPL(uwb_bg_joined); - /* * Print beaconing state. */ diff --git a/include/linux/uwb.h b/include/linux/uwb.h index 7d3ebf046f9a..1719709d60ca 100644 --- a/include/linux/uwb.h +++ b/include/linux/uwb.h @@ -479,7 +479,6 @@ ssize_t uwb_rc_vcmd(struct uwb_rc *rc, const char *cmd_name, struct uwb_rccb *cmd, size_t cmd_size, u8 expected_type, u16 expected_event, struct uwb_rceb **preply); -int uwb_bg_joined(struct uwb_rc *rc); size_t __uwb_addr_print(char *, size_t, const unsigned char *, int); @@ -568,7 +567,9 @@ static inline bool uwb_rsv_is_owner(struct uwb_rsv *rsv) } /** - * Events generated by UWB that can be passed to any listeners + * enum uwb_notifs - UWB events that can be passed to any listeners + * @UWB_NOTIF_ONAIR: a new neighbour has joined the beacon group. + * @UWB_NOTIF_OFFAIR: a neighbour has left the beacon group. * * Higher layers can register callback functions with the radio * controller using uwb_notifs_register(). The radio controller @@ -576,8 +577,6 @@ static inline bool uwb_rsv_is_owner(struct uwb_rsv *rsv) * nodes when an event occurs. */ enum uwb_notifs { - UWB_NOTIF_BG_JOIN = 0, /* radio controller joined a beacon group */ - UWB_NOTIF_BG_LEAVE = 1, /* radio controller left a beacon group */ UWB_NOTIF_ONAIR, UWB_NOTIF_OFFAIR, }; -- cgit v1.2.3 From 758b2cdc6f6a22c702bd8f2344382fb1270b2161 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Nov 2008 02:35:04 +1030 Subject: sched: wrap sched_group and sched_domain cpumask accesses. Impact: trivial wrap of member accesses This eases the transition in the next patch. We also get rid of a temporary cpumask in find_idlest_cpu() thanks to for_each_cpu_and, and sched_balance_self() due to getting weight before setting sd to NULL. Signed-off-by: Rusty Russell Signed-off-by: Ingo Molnar --- include/linux/sched.h | 10 +++++ kernel/sched.c | 114 ++++++++++++++++++++++++-------------------------- kernel/sched_fair.c | 10 ++--- kernel/sched_rt.c | 3 +- kernel/sched_stats.h | 3 +- 5 files changed, 73 insertions(+), 67 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 4ce5c603c51a..2b95aa9f779b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -786,6 +786,11 @@ struct sched_group { u32 reciprocal_cpu_power; }; +static inline struct cpumask *sched_group_cpus(struct sched_group *sg) +{ + return &sg->cpumask; +} + enum sched_domain_level { SD_LV_NONE = 0, SD_LV_SIBLING, @@ -866,6 +871,11 @@ struct sched_domain { #endif }; +static inline struct cpumask *sched_domain_span(struct sched_domain *sd) +{ + return &sd->span; +} + extern void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, struct sched_domain_attr *dattr_new); extern int arch_reinit_sched_domains(void); diff --git a/kernel/sched.c b/kernel/sched.c index a2de33d05340..575f38acf4da 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1501,7 +1501,7 @@ static int tg_shares_up(struct task_group *tg, void *data) struct sched_domain *sd = data; int i; - for_each_cpu_mask(i, sd->span) { + for_each_cpu(i, sched_domain_span(sd)) { /* * If there are currently no tasks on the cpu pretend there * is one of average load so that when a new task gets to @@ -1522,7 +1522,7 @@ static int tg_shares_up(struct task_group *tg, void *data) if (!sd->parent || !(sd->parent->flags & SD_LOAD_BALANCE)) shares = tg->shares; - for_each_cpu_mask(i, sd->span) + for_each_cpu(i, sched_domain_span(sd)) update_group_shares_cpu(tg, i, shares, rq_weight); return 0; @@ -2053,15 +2053,17 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p, int this_cpu) int i; /* Skip over this group if it has no CPUs allowed */ - if (!cpus_intersects(group->cpumask, p->cpus_allowed)) + if (!cpumask_intersects(sched_group_cpus(group), + &p->cpus_allowed)) continue; - local_group = cpu_isset(this_cpu, group->cpumask); + local_group = cpumask_test_cpu(this_cpu, + sched_group_cpus(group)); /* Tally up the load of all CPUs in the group */ avg_load = 0; - for_each_cpu(i, &group->cpumask) { + for_each_cpu(i, sched_group_cpus(group)) { /* Bias balancing toward cpus of our domain */ if (local_group) load = source_load(i, load_idx); @@ -2093,17 +2095,14 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p, int this_cpu) * find_idlest_cpu - find the idlest cpu among the cpus in group. */ static int -find_idlest_cpu(struct sched_group *group, struct task_struct *p, int this_cpu, - cpumask_t *tmp) +find_idlest_cpu(struct sched_group *group, struct task_struct *p, int this_cpu) { unsigned long load, min_load = ULONG_MAX; int idlest = -1; int i; /* Traverse only the allowed CPUs */ - cpus_and(*tmp, group->cpumask, p->cpus_allowed); - - for_each_cpu(i, tmp) { + for_each_cpu_and(i, sched_group_cpus(group), &p->cpus_allowed) { load = weighted_cpuload(i); if (load < min_load || (load == min_load && i == this_cpu)) { @@ -2145,7 +2144,6 @@ static int sched_balance_self(int cpu, int flag) update_shares(sd); while (sd) { - cpumask_t span, tmpmask; struct sched_group *group; int new_cpu, weight; @@ -2154,14 +2152,13 @@ static int sched_balance_self(int cpu, int flag) continue; } - span = sd->span; group = find_idlest_group(sd, t, cpu); if (!group) { sd = sd->child; continue; } - new_cpu = find_idlest_cpu(group, t, cpu, &tmpmask); + new_cpu = find_idlest_cpu(group, t, cpu); if (new_cpu == -1 || new_cpu == cpu) { /* Now try balancing at a lower domain level of cpu */ sd = sd->child; @@ -2170,10 +2167,10 @@ static int sched_balance_self(int cpu, int flag) /* Now try balancing at a lower domain level of new_cpu */ cpu = new_cpu; + weight = cpumask_weight(sched_domain_span(sd)); sd = NULL; - weight = cpus_weight(span); for_each_domain(cpu, tmp) { - if (weight <= cpus_weight(tmp->span)) + if (weight <= cpumask_weight(sched_domain_span(tmp))) break; if (tmp->flags & flag) sd = tmp; @@ -2218,7 +2215,7 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync) cpu = task_cpu(p); for_each_domain(this_cpu, sd) { - if (cpu_isset(cpu, sd->span)) { + if (cpumask_test_cpu(cpu, sched_domain_span(sd))) { update_shares(sd); break; } @@ -2266,7 +2263,7 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync) else { struct sched_domain *sd; for_each_domain(this_cpu, sd) { - if (cpu_isset(cpu, sd->span)) { + if (cpumask_test_cpu(cpu, sched_domain_span(sd))) { schedstat_inc(sd, ttwu_wake_remote); break; } @@ -3109,10 +3106,11 @@ find_busiest_group(struct sched_domain *sd, int this_cpu, unsigned long sum_avg_load_per_task; unsigned long avg_load_per_task; - local_group = cpu_isset(this_cpu, group->cpumask); + local_group = cpumask_test_cpu(this_cpu, + sched_group_cpus(group)); if (local_group) - balance_cpu = first_cpu(group->cpumask); + balance_cpu = cpumask_first(sched_group_cpus(group)); /* Tally up the load of all CPUs in the group */ sum_weighted_load = sum_nr_running = avg_load = 0; @@ -3121,13 +3119,8 @@ find_busiest_group(struct sched_domain *sd, int this_cpu, max_cpu_load = 0; min_cpu_load = ~0UL; - for_each_cpu(i, &group->cpumask) { - struct rq *rq; - - if (!cpu_isset(i, *cpus)) - continue; - - rq = cpu_rq(i); + for_each_cpu_and(i, sched_group_cpus(group), cpus) { + struct rq *rq = cpu_rq(i); if (*sd_idle && rq->nr_running) *sd_idle = 0; @@ -3238,8 +3231,8 @@ find_busiest_group(struct sched_domain *sd, int this_cpu, */ if ((sum_nr_running < min_nr_running) || (sum_nr_running == min_nr_running && - first_cpu(group->cpumask) < - first_cpu(group_min->cpumask))) { + cpumask_first(sched_group_cpus(group)) < + cpumask_first(sched_group_cpus(group_min)))) { group_min = group; min_nr_running = sum_nr_running; min_load_per_task = sum_weighted_load / @@ -3254,8 +3247,8 @@ find_busiest_group(struct sched_domain *sd, int this_cpu, if (sum_nr_running <= group_capacity - 1) { if (sum_nr_running > leader_nr_running || (sum_nr_running == leader_nr_running && - first_cpu(group->cpumask) > - first_cpu(group_leader->cpumask))) { + cpumask_first(sched_group_cpus(group)) > + cpumask_first(sched_group_cpus(group_leader)))) { group_leader = group; leader_nr_running = sum_nr_running; } @@ -3400,7 +3393,7 @@ find_busiest_queue(struct sched_group *group, enum cpu_idle_type idle, unsigned long max_load = 0; int i; - for_each_cpu(i, &group->cpumask) { + for_each_cpu(i, sched_group_cpus(group)) { unsigned long wl; if (!cpu_isset(i, *cpus)) @@ -3746,7 +3739,7 @@ static void active_load_balance(struct rq *busiest_rq, int busiest_cpu) /* Search for an sd spanning us and the target CPU. */ for_each_domain(target_cpu, sd) { if ((sd->flags & SD_LOAD_BALANCE) && - cpu_isset(busiest_cpu, sd->span)) + cpumask_test_cpu(busiest_cpu, sched_domain_span(sd))) break; } @@ -6618,7 +6611,7 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level, struct sched_group *group = sd->groups; char str[256]; - cpulist_scnprintf(str, sizeof(str), sd->span); + cpulist_scnprintf(str, sizeof(str), *sched_domain_span(sd)); cpus_clear(*groupmask); printk(KERN_DEBUG "%*s domain %d: ", level, "", level); @@ -6633,11 +6626,11 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level, printk(KERN_CONT "span %s level %s\n", str, sd->name); - if (!cpu_isset(cpu, sd->span)) { + if (!cpumask_test_cpu(cpu, sched_domain_span(sd))) { printk(KERN_ERR "ERROR: domain->span does not contain " "CPU%d\n", cpu); } - if (!cpu_isset(cpu, group->cpumask)) { + if (!cpumask_test_cpu(cpu, sched_group_cpus(group))) { printk(KERN_ERR "ERROR: domain->groups does not contain" " CPU%d\n", cpu); } @@ -6657,31 +6650,32 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level, break; } - if (!cpus_weight(group->cpumask)) { + if (!cpumask_weight(sched_group_cpus(group))) { printk(KERN_CONT "\n"); printk(KERN_ERR "ERROR: empty group\n"); break; } - if (cpus_intersects(*groupmask, group->cpumask)) { + if (cpumask_intersects(groupmask, sched_group_cpus(group))) { printk(KERN_CONT "\n"); printk(KERN_ERR "ERROR: repeated CPUs\n"); break; } - cpus_or(*groupmask, *groupmask, group->cpumask); + cpumask_or(groupmask, groupmask, sched_group_cpus(group)); - cpulist_scnprintf(str, sizeof(str), group->cpumask); + cpulist_scnprintf(str, sizeof(str), *sched_group_cpus(group)); printk(KERN_CONT " %s", str); group = group->next; } while (group != sd->groups); printk(KERN_CONT "\n"); - if (!cpus_equal(sd->span, *groupmask)) + if (!cpumask_equal(sched_domain_span(sd), groupmask)) printk(KERN_ERR "ERROR: groups don't span domain->span\n"); - if (sd->parent && !cpus_subset(*groupmask, sd->parent->span)) + if (sd->parent && + !cpumask_subset(groupmask, sched_domain_span(sd->parent))) printk(KERN_ERR "ERROR: parent span is not a superset " "of domain->span\n"); return 0; @@ -6721,7 +6715,7 @@ static void sched_domain_debug(struct sched_domain *sd, int cpu) static int sd_degenerate(struct sched_domain *sd) { - if (cpus_weight(sd->span) == 1) + if (cpumask_weight(sched_domain_span(sd)) == 1) return 1; /* Following flags need at least 2 groups */ @@ -6752,7 +6746,7 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent) if (sd_degenerate(parent)) return 1; - if (!cpus_equal(sd->span, parent->span)) + if (!cpumask_equal(sched_domain_span(sd), sched_domain_span(parent))) return 0; /* Does parent contain flags not in child? */ @@ -6913,10 +6907,10 @@ init_sched_build_groups(const cpumask_t *span, const cpumask_t *cpu_map, int group = group_fn(i, cpu_map, &sg, tmpmask); int j; - if (cpu_isset(i, *covered)) + if (cpumask_test_cpu(i, covered)) continue; - cpus_clear(sg->cpumask); + cpumask_clear(sched_group_cpus(sg)); sg->__cpu_power = 0; for_each_cpu(j, span) { @@ -6924,7 +6918,7 @@ init_sched_build_groups(const cpumask_t *span, const cpumask_t *cpu_map, continue; cpu_set(j, *covered); - cpu_set(j, sg->cpumask); + cpumask_set_cpu(j, sched_group_cpus(sg)); } if (!first) first = sg; @@ -7119,11 +7113,11 @@ static void init_numa_sched_groups_power(struct sched_group *group_head) if (!sg) return; do { - for_each_cpu(j, &sg->cpumask) { + for_each_cpu(j, sched_group_cpus(sg)) { struct sched_domain *sd; sd = &per_cpu(phys_domains, j); - if (j != first_cpu(sd->groups->cpumask)) { + if (j != cpumask_first(sched_group_cpus(sd->groups))) { /* * Only add "power" once for each * physical package. @@ -7200,7 +7194,7 @@ static void init_sched_groups_power(int cpu, struct sched_domain *sd) WARN_ON(!sd || !sd->groups); - if (cpu != first_cpu(sd->groups->cpumask)) + if (cpu != cpumask_first(sched_group_cpus(sd->groups))) return; child = sd->child; @@ -7372,7 +7366,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, sd = &per_cpu(allnodes_domains, i); SD_INIT(sd, ALLNODES); set_domain_attribute(sd, attr); - sd->span = *cpu_map; + cpumask_copy(sched_domain_span(sd), cpu_map); cpu_to_allnodes_group(i, cpu_map, &sd->groups, tmpmask); p = sd; sd_allnodes = 1; @@ -7382,18 +7376,19 @@ static int __build_sched_domains(const cpumask_t *cpu_map, sd = &per_cpu(node_domains, i); SD_INIT(sd, NODE); set_domain_attribute(sd, attr); - sched_domain_node_span(cpu_to_node(i), &sd->span); + sched_domain_node_span(cpu_to_node(i), sched_domain_span(sd)); sd->parent = p; if (p) p->child = sd; - cpus_and(sd->span, sd->span, *cpu_map); + cpumask_and(sched_domain_span(sd), + sched_domain_span(sd), cpu_map); #endif p = sd; sd = &per_cpu(phys_domains, i); SD_INIT(sd, CPU); set_domain_attribute(sd, attr); - sd->span = *nodemask; + cpumask_copy(sched_domain_span(sd), nodemask); sd->parent = p; if (p) p->child = sd; @@ -7404,8 +7399,9 @@ static int __build_sched_domains(const cpumask_t *cpu_map, sd = &per_cpu(core_domains, i); SD_INIT(sd, MC); set_domain_attribute(sd, attr); - sd->span = cpu_coregroup_map(i); - cpus_and(sd->span, sd->span, *cpu_map); + *sched_domain_span(sd) = cpu_coregroup_map(i); + cpumask_and(sched_domain_span(sd), + sched_domain_span(sd), cpu_map); sd->parent = p; p->child = sd; cpu_to_core_group(i, cpu_map, &sd->groups, tmpmask); @@ -7416,8 +7412,8 @@ static int __build_sched_domains(const cpumask_t *cpu_map, sd = &per_cpu(cpu_domains, i); SD_INIT(sd, SIBLING); set_domain_attribute(sd, attr); - sd->span = per_cpu(cpu_sibling_map, i); - cpus_and(sd->span, sd->span, *cpu_map); + cpumask_and(sched_domain_span(sd), + &per_cpu(cpu_sibling_map, i), cpu_map); sd->parent = p; p->child = sd; cpu_to_cpu_group(i, cpu_map, &sd->groups, tmpmask); @@ -7503,7 +7499,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, sd->groups = sg; } sg->__cpu_power = 0; - sg->cpumask = *nodemask; + cpumask_copy(sched_group_cpus(sg), nodemask); sg->next = sg; cpus_or(*covered, *covered, *nodemask); prev = sg; @@ -7530,7 +7526,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, goto error; } sg->__cpu_power = 0; - sg->cpumask = *tmpmask; + cpumask_copy(sched_group_cpus(sg), tmpmask); sg->next = prev->next; cpus_or(*covered, *covered, *tmpmask); prev->next = sg; diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index 98345e45b059..bba00402ed90 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -1024,7 +1024,6 @@ static void yield_task_fair(struct rq *rq) #if defined(ARCH_HAS_SCHED_WAKE_IDLE) static int wake_idle(int cpu, struct task_struct *p) { - cpumask_t tmp; struct sched_domain *sd; int i; @@ -1044,10 +1043,9 @@ static int wake_idle(int cpu, struct task_struct *p) if ((sd->flags & SD_WAKE_IDLE) || ((sd->flags & SD_WAKE_IDLE_FAR) && !task_hot(p, task_rq(p)->clock, sd))) { - cpus_and(tmp, sd->span, p->cpus_allowed); - cpus_and(tmp, tmp, cpu_active_map); - for_each_cpu_mask_nr(i, tmp) { - if (idle_cpu(i)) { + for_each_cpu_and(i, sched_domain_span(sd), + &p->cpus_allowed) { + if (cpu_active(i) && idle_cpu(i)) { if (i != task_cpu(p)) { schedstat_inc(p, se.nr_wakeups_idle); @@ -1240,7 +1238,7 @@ static int select_task_rq_fair(struct task_struct *p, int sync) * this_cpu and prev_cpu are present in: */ for_each_domain(this_cpu, sd) { - if (cpu_isset(prev_cpu, sd->span)) { + if (cpumask_test_cpu(prev_cpu, sched_domain_span(sd))) { this_sd = sd; break; } diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index 2bdd44423599..4cd813abc23a 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c @@ -1017,7 +1017,8 @@ static int find_lowest_rq(struct task_struct *task) cpumask_t domain_mask; int best_cpu; - cpus_and(domain_mask, sd->span, *lowest_mask); + cpumask_and(&domain_mask, sched_domain_span(sd), + lowest_mask); best_cpu = pick_optimal_cpu(this_cpu, &domain_mask); diff --git a/kernel/sched_stats.h b/kernel/sched_stats.h index 7dbf72a2b02c..ce340835d055 100644 --- a/kernel/sched_stats.h +++ b/kernel/sched_stats.h @@ -42,7 +42,8 @@ static int show_schedstat(struct seq_file *seq, void *v) for_each_domain(cpu, sd) { enum cpu_idle_type itype; - cpumask_scnprintf(mask_str, mask_len, sd->span); + cpumask_scnprintf(mask_str, mask_len, + *sched_domain_span(sd)); seq_printf(seq, "domain%d %s", dcount++, mask_str); for (itype = CPU_IDLE; itype < CPU_MAX_IDLE_TYPES; itype++) { -- cgit v1.2.3 From 6c99e9ad47d9c082bd096f42fb49e397b05d58a8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Nov 2008 02:35:04 +1030 Subject: sched: convert struct sched_group/sched_domain cpumask_ts to variable bitmaps Impact: (future) size reduction for large NR_CPUS. We move the 'cpumask' member of sched_group to the end, so when we kmalloc it we can do a minimal allocation: saves space for small nr_cpu_ids but big CONFIG_NR_CPUS. Similar trick for 'span' in sched_domain. This isn't quite as good as converting to a cpumask_var_t, as some sched_groups are actually static, but it's safer: we don't have to figure out where to call alloc_cpumask_var/free_cpumask_var. Signed-off-by: Rusty Russell Signed-off-by: Ingo Molnar --- include/linux/sched.h | 11 +++++---- kernel/sched.c | 65 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 48 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 2b95aa9f779b..c5be6c6bc741 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -771,7 +771,6 @@ enum cpu_idle_type { struct sched_group { struct sched_group *next; /* Must be a circular list */ - cpumask_t cpumask; /* * CPU power of this group, SCHED_LOAD_SCALE being max power for a @@ -784,11 +783,13 @@ struct sched_group { * (see include/linux/reciprocal_div.h) */ u32 reciprocal_cpu_power; + + unsigned long cpumask[]; }; static inline struct cpumask *sched_group_cpus(struct sched_group *sg) { - return &sg->cpumask; + return to_cpumask(sg->cpumask); } enum sched_domain_level { @@ -814,7 +815,6 @@ struct sched_domain { struct sched_domain *parent; /* top domain must be null terminated */ struct sched_domain *child; /* bottom domain must be null terminated */ struct sched_group *groups; /* the balancing groups of the domain */ - cpumask_t span; /* span of all CPUs in this domain */ unsigned long min_interval; /* Minimum balance interval ms */ unsigned long max_interval; /* Maximum balance interval ms */ unsigned int busy_factor; /* less balancing by factor if busy */ @@ -869,11 +869,14 @@ struct sched_domain { #ifdef CONFIG_SCHED_DEBUG char *name; #endif + + /* span of all CPUs in this domain */ + unsigned long span[]; }; static inline struct cpumask *sched_domain_span(struct sched_domain *sd) { - return &sd->span; + return to_cpumask(sd->span); } extern void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, diff --git a/kernel/sched.c b/kernel/sched.c index 575f38acf4da..6b9606a6cabf 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -7005,19 +7005,34 @@ static void sched_domain_node_span(int node, cpumask_t *span) int sched_smt_power_savings = 0, sched_mc_power_savings = 0; +/* + * The cpus mask in sched_group and sched_domain hangs off the end. + * FIXME: use cpumask_var_t or dynamic percpu alloc to avoid wasting space + * for nr_cpu_ids < CONFIG_NR_CPUS. + */ +struct static_sched_group { + struct sched_group sg; + DECLARE_BITMAP(cpus, CONFIG_NR_CPUS); +}; + +struct static_sched_domain { + struct sched_domain sd; + DECLARE_BITMAP(span, CONFIG_NR_CPUS); +}; + /* * SMT sched-domains: */ #ifdef CONFIG_SCHED_SMT -static DEFINE_PER_CPU(struct sched_domain, cpu_domains); -static DEFINE_PER_CPU(struct sched_group, sched_group_cpus); +static DEFINE_PER_CPU(struct static_sched_domain, cpu_domains); +static DEFINE_PER_CPU(struct static_sched_group, sched_group_cpus); static int cpu_to_cpu_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, cpumask_t *unused) { if (sg) - *sg = &per_cpu(sched_group_cpus, cpu); + *sg = &per_cpu(sched_group_cpus, cpu).sg; return cpu; } #endif /* CONFIG_SCHED_SMT */ @@ -7026,8 +7041,8 @@ cpu_to_cpu_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, * multi-core sched-domains: */ #ifdef CONFIG_SCHED_MC -static DEFINE_PER_CPU(struct sched_domain, core_domains); -static DEFINE_PER_CPU(struct sched_group, sched_group_core); +static DEFINE_PER_CPU(struct static_sched_domain, core_domains); +static DEFINE_PER_CPU(struct static_sched_group, sched_group_core); #endif /* CONFIG_SCHED_MC */ #if defined(CONFIG_SCHED_MC) && defined(CONFIG_SCHED_SMT) @@ -7041,7 +7056,7 @@ cpu_to_core_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, cpus_and(*mask, *mask, *cpu_map); group = first_cpu(*mask); if (sg) - *sg = &per_cpu(sched_group_core, group); + *sg = &per_cpu(sched_group_core, group).sg; return group; } #elif defined(CONFIG_SCHED_MC) @@ -7050,13 +7065,13 @@ cpu_to_core_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, cpumask_t *unused) { if (sg) - *sg = &per_cpu(sched_group_core, cpu); + *sg = &per_cpu(sched_group_core, cpu).sg; return cpu; } #endif -static DEFINE_PER_CPU(struct sched_domain, phys_domains); -static DEFINE_PER_CPU(struct sched_group, sched_group_phys); +static DEFINE_PER_CPU(struct static_sched_domain, phys_domains); +static DEFINE_PER_CPU(struct static_sched_group, sched_group_phys); static int cpu_to_phys_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, @@ -7075,7 +7090,7 @@ cpu_to_phys_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, group = cpu; #endif if (sg) - *sg = &per_cpu(sched_group_phys, group); + *sg = &per_cpu(sched_group_phys, group).sg; return group; } @@ -7089,7 +7104,7 @@ static DEFINE_PER_CPU(struct sched_domain, node_domains); static struct sched_group ***sched_group_nodes_bycpu; static DEFINE_PER_CPU(struct sched_domain, allnodes_domains); -static DEFINE_PER_CPU(struct sched_group, sched_group_allnodes); +static DEFINE_PER_CPU(struct static_sched_group, sched_group_allnodes); static int cpu_to_allnodes_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, cpumask_t *nodemask) @@ -7101,7 +7116,7 @@ static int cpu_to_allnodes_group(int cpu, const cpumask_t *cpu_map, group = first_cpu(*nodemask); if (sg) - *sg = &per_cpu(sched_group_allnodes, group); + *sg = &per_cpu(sched_group_allnodes, group).sg; return group; } @@ -7116,7 +7131,7 @@ static void init_numa_sched_groups_power(struct sched_group *group_head) for_each_cpu(j, sched_group_cpus(sg)) { struct sched_domain *sd; - sd = &per_cpu(phys_domains, j); + sd = &per_cpu(phys_domains, j).sd; if (j != cpumask_first(sched_group_cpus(sd->groups))) { /* * Only add "power" once for each @@ -7385,7 +7400,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, #endif p = sd; - sd = &per_cpu(phys_domains, i); + sd = &per_cpu(phys_domains, i).sd; SD_INIT(sd, CPU); set_domain_attribute(sd, attr); cpumask_copy(sched_domain_span(sd), nodemask); @@ -7396,7 +7411,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, #ifdef CONFIG_SCHED_MC p = sd; - sd = &per_cpu(core_domains, i); + sd = &per_cpu(core_domains, i).sd; SD_INIT(sd, MC); set_domain_attribute(sd, attr); *sched_domain_span(sd) = cpu_coregroup_map(i); @@ -7409,7 +7424,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, #ifdef CONFIG_SCHED_SMT p = sd; - sd = &per_cpu(cpu_domains, i); + sd = &per_cpu(cpu_domains, i).sd; SD_INIT(sd, SIBLING); set_domain_attribute(sd, attr); cpumask_and(sched_domain_span(sd), @@ -7485,7 +7500,8 @@ static int __build_sched_domains(const cpumask_t *cpu_map, sched_domain_node_span(i, domainspan); cpus_and(*domainspan, *domainspan, *cpu_map); - sg = kmalloc_node(sizeof(struct sched_group), GFP_KERNEL, i); + sg = kmalloc_node(sizeof(struct sched_group) + cpumask_size(), + GFP_KERNEL, i); if (!sg) { printk(KERN_WARNING "Can not alloc domain group for " "node %d\n", i); @@ -7518,7 +7534,8 @@ static int __build_sched_domains(const cpumask_t *cpu_map, if (cpus_empty(*tmpmask)) continue; - sg = kmalloc_node(sizeof(struct sched_group), + sg = kmalloc_node(sizeof(struct sched_group) + + cpumask_size(), GFP_KERNEL, i); if (!sg) { printk(KERN_WARNING @@ -7538,21 +7555,21 @@ static int __build_sched_domains(const cpumask_t *cpu_map, /* Calculate CPU power for physical packages and nodes */ #ifdef CONFIG_SCHED_SMT for_each_cpu(i, cpu_map) { - struct sched_domain *sd = &per_cpu(cpu_domains, i); + struct sched_domain *sd = &per_cpu(cpu_domains, i).sd; init_sched_groups_power(i, sd); } #endif #ifdef CONFIG_SCHED_MC for_each_cpu(i, cpu_map) { - struct sched_domain *sd = &per_cpu(core_domains, i); + struct sched_domain *sd = &per_cpu(core_domains, i).sd; init_sched_groups_power(i, sd); } #endif for_each_cpu(i, cpu_map) { - struct sched_domain *sd = &per_cpu(phys_domains, i); + struct sched_domain *sd = &per_cpu(phys_domains, i).sd; init_sched_groups_power(i, sd); } @@ -7574,11 +7591,11 @@ static int __build_sched_domains(const cpumask_t *cpu_map, for_each_cpu(i, cpu_map) { struct sched_domain *sd; #ifdef CONFIG_SCHED_SMT - sd = &per_cpu(cpu_domains, i); + sd = &per_cpu(cpu_domains, i).sd; #elif defined(CONFIG_SCHED_MC) - sd = &per_cpu(core_domains, i); + sd = &per_cpu(core_domains, i).sd; #else - sd = &per_cpu(phys_domains, i); + sd = &per_cpu(phys_domains, i).sd; #endif cpu_attach_domain(sd, rd, i); } -- cgit v1.2.3 From 6a7b3dc3440f7b5a9b67594af01ed562cdeb41e4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Nov 2008 02:35:04 +1030 Subject: sched: convert nohz_cpu_mask to cpumask_var_t. Impact: (future) size reduction for large NR_CPUS. Dynamically allocating cpumasks (when CONFIG_CPUMASK_OFFSTACK) saves space for small nr_cpu_ids but big CONFIG_NR_CPUS. cpumask_var_t is just a struct cpumask for !CONFIG_CPUMASK_OFFSTACK. Signed-off-by: Rusty Russell Signed-off-by: Ingo Molnar --- include/linux/sched.h | 2 +- kernel/rcuclassic.c | 2 +- kernel/sched.c | 7 +++++-- kernel/time/tick-sched.c | 10 +++++----- 4 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index c5be6c6bc741..1e33e2cb7f8c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -249,7 +249,7 @@ extern void init_idle_bootup_task(struct task_struct *idle); extern int runqueue_is_locked(void); extern void task_rq_unlock_wait(struct task_struct *p); -extern cpumask_t nohz_cpu_mask; +extern cpumask_var_t nohz_cpu_mask; #if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ) extern int select_nohz_load_balancer(int cpu); #else diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c index e503a002f330..c03ca3e61919 100644 --- a/kernel/rcuclassic.c +++ b/kernel/rcuclassic.c @@ -393,7 +393,7 @@ static void rcu_start_batch(struct rcu_ctrlblk *rcp) * unnecessarily. */ smp_mb(); - cpus_andnot(rcp->cpumask, cpu_online_map, nohz_cpu_mask); + cpumask_andnot(&rcp->cpumask, cpu_online_mask, nohz_cpu_mask); rcp->signaled = 0; } diff --git a/kernel/sched.c b/kernel/sched.c index 6b9606a6cabf..2723d7a4a42d 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -5870,9 +5870,9 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu) * indicates which cpus entered this state. This is used * in the rcu update to wait only for active cpus. For system * which do not switch off the HZ timer nohz_cpu_mask should - * always be CPU_MASK_NONE. + * always be CPU_BITS_NONE. */ -cpumask_t nohz_cpu_mask = CPU_MASK_NONE; +cpumask_var_t nohz_cpu_mask; /* * Increase the granularity value when there are more CPUs, @@ -8274,6 +8274,9 @@ void __init sched_init(void) */ current->sched_class = &fair_sched_class; + /* Allocate the nohz_cpu_mask if CONFIG_CPUMASK_OFFSTACK */ + alloc_bootmem_cpumask_var(&nohz_cpu_mask); + scheduler_running = 1; } diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 342fc9ccab46..70f872c71f4e 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -144,7 +144,7 @@ void tick_nohz_update_jiffies(void) if (!ts->tick_stopped) return; - cpu_clear(cpu, nohz_cpu_mask); + cpumask_clear_cpu(cpu, nohz_cpu_mask); now = ktime_get(); ts->idle_waketime = now; @@ -283,7 +283,7 @@ void tick_nohz_stop_sched_tick(int inidle) if ((long)delta_jiffies >= 1) { if (delta_jiffies > 1) - cpu_set(cpu, nohz_cpu_mask); + cpumask_set_cpu(cpu, nohz_cpu_mask); /* * nohz_stop_sched_tick can be called several times before * the nohz_restart_sched_tick is called. This happens when @@ -296,7 +296,7 @@ void tick_nohz_stop_sched_tick(int inidle) /* * sched tick not stopped! */ - cpu_clear(cpu, nohz_cpu_mask); + cpumask_clear_cpu(cpu, nohz_cpu_mask); goto out; } @@ -354,7 +354,7 @@ void tick_nohz_stop_sched_tick(int inidle) * softirq. */ tick_do_update_jiffies64(ktime_get()); - cpu_clear(cpu, nohz_cpu_mask); + cpumask_clear_cpu(cpu, nohz_cpu_mask); } raise_softirq_irqoff(TIMER_SOFTIRQ); out: @@ -432,7 +432,7 @@ void tick_nohz_restart_sched_tick(void) select_nohz_load_balancer(0); now = ktime_get(); tick_do_update_jiffies64(now); - cpu_clear(cpu, nohz_cpu_mask); + cpumask_clear_cpu(cpu, nohz_cpu_mask); /* * We stopped the tick in idle. Update process times would miss the -- cgit v1.2.3 From 96f874e26428ab5d2db681c100210c254775e154 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Nov 2008 02:35:14 +1030 Subject: sched: convert remaining old-style cpumask operators Impact: Trivial API conversion NR_CPUS -> nr_cpu_ids cpumask_t -> struct cpumask sizeof(cpumask_t) -> cpumask_size() cpumask_a = cpumask_b -> cpumask_copy(&cpumask_a, &cpumask_b) cpu_set() -> cpumask_set_cpu() first_cpu() -> cpumask_first() cpumask_of_cpu() -> cpumask_of() cpus_* -> cpumask_* There are some FIXMEs where we all archs to complete infrastructure (patches have been sent): cpu_coregroup_map -> cpu_coregroup_mask node_to_cpumask* -> cpumask_of_node There is also one FIXME where we pass an array of cpumasks to partition_sched_domains(): this implies knowing the definition of 'struct cpumask' and the size of a cpumask. This will be fixed in a future patch. Signed-off-by: Rusty Russell Signed-off-by: Ingo Molnar --- include/linux/sched.h | 16 ++-- kernel/sched.c | 212 +++++++++++++++++++++++++++----------------------- kernel/sched_fair.c | 4 +- kernel/sched_rt.c | 18 ++--- 4 files changed, 132 insertions(+), 118 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 1e33e2cb7f8c..4b7b0187374c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -879,7 +879,7 @@ static inline struct cpumask *sched_domain_span(struct sched_domain *sd) return to_cpumask(sd->span); } -extern void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, +extern void partition_sched_domains(int ndoms_new, struct cpumask *doms_new, struct sched_domain_attr *dattr_new); extern int arch_reinit_sched_domains(void); @@ -888,7 +888,7 @@ extern int arch_reinit_sched_domains(void); struct sched_domain_attr; static inline void -partition_sched_domains(int ndoms_new, cpumask_t *doms_new, +partition_sched_domains(int ndoms_new, struct cpumask *doms_new, struct sched_domain_attr *dattr_new) { } @@ -970,7 +970,7 @@ struct sched_class { void (*task_wake_up) (struct rq *this_rq, struct task_struct *task); void (*set_cpus_allowed)(struct task_struct *p, - const cpumask_t *newmask); + const struct cpumask *newmask); void (*rq_online)(struct rq *rq); void (*rq_offline)(struct rq *rq); @@ -1612,12 +1612,12 @@ extern cputime_t task_gtime(struct task_struct *p); #ifdef CONFIG_SMP extern int set_cpus_allowed_ptr(struct task_struct *p, - const cpumask_t *new_mask); + const struct cpumask *new_mask); #else static inline int set_cpus_allowed_ptr(struct task_struct *p, - const cpumask_t *new_mask) + const struct cpumask *new_mask) { - if (!cpu_isset(0, *new_mask)) + if (!cpumask_test_cpu(0, new_mask)) return -EINVAL; return 0; } @@ -2230,8 +2230,8 @@ __trace_special(void *__tr, void *__data, } #endif -extern long sched_setaffinity(pid_t pid, const cpumask_t *new_mask); -extern long sched_getaffinity(pid_t pid, cpumask_t *mask); +extern long sched_setaffinity(pid_t pid, const struct cpumask *new_mask); +extern long sched_getaffinity(pid_t pid, struct cpumask *mask); extern int sched_mc_power_savings, sched_smt_power_savings; diff --git a/kernel/sched.c b/kernel/sched.c index f2be61870030..eba6a156d334 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2829,7 +2829,7 @@ static void sched_migrate_task(struct task_struct *p, int dest_cpu) struct rq *rq; rq = task_rq_lock(p, &flags); - if (!cpu_isset(dest_cpu, p->cpus_allowed) + if (!cpumask_test_cpu(dest_cpu, &p->cpus_allowed) || unlikely(!cpu_active(dest_cpu))) goto out; @@ -2895,7 +2895,7 @@ int can_migrate_task(struct task_struct *p, struct rq *rq, int this_cpu, * 2) cannot be migrated to this CPU due to cpus_allowed, or * 3) are cache-hot on their current CPU. */ - if (!cpu_isset(this_cpu, p->cpus_allowed)) { + if (!cpumask_test_cpu(this_cpu, &p->cpus_allowed)) { schedstat_inc(p, se.nr_failed_migrations_affine); return 0; } @@ -3070,7 +3070,7 @@ static int move_one_task(struct rq *this_rq, int this_cpu, struct rq *busiest, static struct sched_group * find_busiest_group(struct sched_domain *sd, int this_cpu, unsigned long *imbalance, enum cpu_idle_type idle, - int *sd_idle, const cpumask_t *cpus, int *balance) + int *sd_idle, const struct cpumask *cpus, int *balance) { struct sched_group *busiest = NULL, *this = NULL, *group = sd->groups; unsigned long max_load, avg_load, total_load, this_load, total_pwr; @@ -3387,7 +3387,7 @@ ret: */ static struct rq * find_busiest_queue(struct sched_group *group, enum cpu_idle_type idle, - unsigned long imbalance, const cpumask_t *cpus) + unsigned long imbalance, const struct cpumask *cpus) { struct rq *busiest = NULL, *rq; unsigned long max_load = 0; @@ -3396,7 +3396,7 @@ find_busiest_queue(struct sched_group *group, enum cpu_idle_type idle, for_each_cpu(i, sched_group_cpus(group)) { unsigned long wl; - if (!cpu_isset(i, *cpus)) + if (!cpumask_test_cpu(i, cpus)) continue; rq = cpu_rq(i); @@ -3426,7 +3426,7 @@ find_busiest_queue(struct sched_group *group, enum cpu_idle_type idle, */ static int load_balance(int this_cpu, struct rq *this_rq, struct sched_domain *sd, enum cpu_idle_type idle, - int *balance, cpumask_t *cpus) + int *balance, struct cpumask *cpus) { int ld_moved, all_pinned = 0, active_balance = 0, sd_idle = 0; struct sched_group *group; @@ -3434,7 +3434,7 @@ static int load_balance(int this_cpu, struct rq *this_rq, struct rq *busiest; unsigned long flags; - cpus_setall(*cpus); + cpumask_setall(cpus); /* * When power savings policy is enabled for the parent domain, idle @@ -3494,8 +3494,8 @@ redo: /* All tasks on this runqueue were pinned by CPU affinity */ if (unlikely(all_pinned)) { - cpu_clear(cpu_of(busiest), *cpus); - if (!cpus_empty(*cpus)) + cpumask_clear_cpu(cpu_of(busiest), cpus); + if (!cpumask_empty(cpus)) goto redo; goto out_balanced; } @@ -3512,7 +3512,8 @@ redo: /* don't kick the migration_thread, if the curr * task on busiest cpu can't be moved to this_cpu */ - if (!cpu_isset(this_cpu, busiest->curr->cpus_allowed)) { + if (!cpumask_test_cpu(this_cpu, + &busiest->curr->cpus_allowed)) { spin_unlock_irqrestore(&busiest->lock, flags); all_pinned = 1; goto out_one_pinned; @@ -3587,7 +3588,7 @@ out: */ static int load_balance_newidle(int this_cpu, struct rq *this_rq, struct sched_domain *sd, - cpumask_t *cpus) + struct cpumask *cpus) { struct sched_group *group; struct rq *busiest = NULL; @@ -3596,7 +3597,7 @@ load_balance_newidle(int this_cpu, struct rq *this_rq, struct sched_domain *sd, int sd_idle = 0; int all_pinned = 0; - cpus_setall(*cpus); + cpumask_setall(cpus); /* * When power savings policy is enabled for the parent domain, idle @@ -3640,8 +3641,8 @@ redo: double_unlock_balance(this_rq, busiest); if (unlikely(all_pinned)) { - cpu_clear(cpu_of(busiest), *cpus); - if (!cpus_empty(*cpus)) + cpumask_clear_cpu(cpu_of(busiest), cpus); + if (!cpumask_empty(cpus)) goto redo; } } @@ -5376,7 +5377,7 @@ out_unlock: return retval; } -long sched_setaffinity(pid_t pid, const cpumask_t *in_mask) +long sched_setaffinity(pid_t pid, const struct cpumask *in_mask) { cpumask_var_t cpus_allowed, new_mask; struct task_struct *p; @@ -5445,13 +5446,13 @@ out_put_task: } static int get_user_cpu_mask(unsigned long __user *user_mask_ptr, unsigned len, - cpumask_t *new_mask) + struct cpumask *new_mask) { - if (len < sizeof(cpumask_t)) { - memset(new_mask, 0, sizeof(cpumask_t)); - } else if (len > sizeof(cpumask_t)) { - len = sizeof(cpumask_t); - } + if (len < cpumask_size()) + cpumask_clear(new_mask); + else if (len > cpumask_size()) + len = cpumask_size(); + return copy_from_user(new_mask, user_mask_ptr, len) ? -EFAULT : 0; } @@ -5477,7 +5478,7 @@ asmlinkage long sys_sched_setaffinity(pid_t pid, unsigned int len, return retval; } -long sched_getaffinity(pid_t pid, cpumask_t *mask) +long sched_getaffinity(pid_t pid, struct cpumask *mask) { struct task_struct *p; int retval; @@ -5494,7 +5495,7 @@ long sched_getaffinity(pid_t pid, cpumask_t *mask) if (retval) goto out_unlock; - cpus_and(*mask, p->cpus_allowed, cpu_online_map); + cpumask_and(mask, &p->cpus_allowed, cpu_online_mask); out_unlock: read_unlock(&tasklist_lock); @@ -5872,7 +5873,7 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu) idle->se.exec_start = sched_clock(); idle->prio = idle->normal_prio = MAX_PRIO; - idle->cpus_allowed = cpumask_of_cpu(cpu); + cpumask_copy(&idle->cpus_allowed, cpumask_of(cpu)); __set_task_cpu(idle, cpu); rq->curr = rq->idle = idle; @@ -5956,7 +5957,7 @@ static inline void sched_init_granularity(void) * task must not exit() & deallocate itself prematurely. The * call is not atomic; no spinlocks may be held. */ -int set_cpus_allowed_ptr(struct task_struct *p, const cpumask_t *new_mask) +int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask) { struct migration_req req; unsigned long flags; @@ -5964,13 +5965,13 @@ int set_cpus_allowed_ptr(struct task_struct *p, const cpumask_t *new_mask) int ret = 0; rq = task_rq_lock(p, &flags); - if (!cpus_intersects(*new_mask, cpu_online_map)) { + if (!cpumask_intersects(new_mask, cpu_online_mask)) { ret = -EINVAL; goto out; } if (unlikely((p->flags & PF_THREAD_BOUND) && p != current && - !cpus_equal(p->cpus_allowed, *new_mask))) { + !cpumask_equal(&p->cpus_allowed, new_mask))) { ret = -EINVAL; goto out; } @@ -5978,12 +5979,12 @@ int set_cpus_allowed_ptr(struct task_struct *p, const cpumask_t *new_mask) if (p->sched_class->set_cpus_allowed) p->sched_class->set_cpus_allowed(p, new_mask); else { - p->cpus_allowed = *new_mask; - p->rt.nr_cpus_allowed = cpus_weight(*new_mask); + cpumask_copy(&p->cpus_allowed, new_mask); + p->rt.nr_cpus_allowed = cpumask_weight(new_mask); } /* Can the task run on the task's current CPU? If so, we're done */ - if (cpu_isset(task_cpu(p), *new_mask)) + if (cpumask_test_cpu(task_cpu(p), new_mask)) goto out; if (migrate_task(p, cpumask_any_and(cpu_online_mask, new_mask), &req)) { @@ -6028,7 +6029,7 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) if (task_cpu(p) != src_cpu) goto done; /* Affinity changed (again). */ - if (!cpu_isset(dest_cpu, p->cpus_allowed)) + if (!cpumask_test_cpu(dest_cpu, &p->cpus_allowed)) goto fail; on_rq = p->se.on_rq; @@ -6629,13 +6630,13 @@ early_initcall(migration_init); #ifdef CONFIG_SCHED_DEBUG static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level, - cpumask_t *groupmask) + struct cpumask *groupmask) { struct sched_group *group = sd->groups; char str[256]; cpulist_scnprintf(str, sizeof(str), *sched_domain_span(sd)); - cpus_clear(*groupmask); + cpumask_clear(groupmask); printk(KERN_DEBUG "%*s domain %d: ", level, "", level); @@ -6936,24 +6937,25 @@ __setup("isolcpus=", isolated_cpu_setup); /* * init_sched_build_groups takes the cpumask we wish to span, and a pointer * to a function which identifies what group(along with sched group) a CPU - * belongs to. The return value of group_fn must be a >= 0 and < NR_CPUS - * (due to the fact that we keep track of groups covered with a cpumask_t). + * belongs to. The return value of group_fn must be a >= 0 and < nr_cpu_ids + * (due to the fact that we keep track of groups covered with a struct cpumask). * * init_sched_build_groups will build a circular linked list of the groups * covered by the given span, and will set each group's ->cpumask correctly, * and ->cpu_power to 0. */ static void -init_sched_build_groups(const cpumask_t *span, const cpumask_t *cpu_map, - int (*group_fn)(int cpu, const cpumask_t *cpu_map, +init_sched_build_groups(const struct cpumask *span, + const struct cpumask *cpu_map, + int (*group_fn)(int cpu, const struct cpumask *cpu_map, struct sched_group **sg, - cpumask_t *tmpmask), - cpumask_t *covered, cpumask_t *tmpmask) + struct cpumask *tmpmask), + struct cpumask *covered, struct cpumask *tmpmask) { struct sched_group *first = NULL, *last = NULL; int i; - cpus_clear(*covered); + cpumask_clear(covered); for_each_cpu(i, span) { struct sched_group *sg; @@ -6970,7 +6972,7 @@ init_sched_build_groups(const cpumask_t *span, const cpumask_t *cpu_map, if (group_fn(j, cpu_map, NULL, tmpmask) != group) continue; - cpu_set(j, *covered); + cpumask_set_cpu(j, covered); cpumask_set_cpu(j, sched_group_cpus(sg)); } if (!first) @@ -7035,9 +7037,10 @@ static int find_next_best_node(int node, nodemask_t *used_nodes) * should be one that prevents unnecessary balancing, but also spreads tasks * out optimally. */ -static void sched_domain_node_span(int node, cpumask_t *span) +static void sched_domain_node_span(int node, struct cpumask *span) { nodemask_t used_nodes; + /* FIXME: use cpumask_of_node() */ node_to_cpumask_ptr(nodemask, node); int i; @@ -7081,8 +7084,8 @@ static DEFINE_PER_CPU(struct static_sched_domain, cpu_domains); static DEFINE_PER_CPU(struct static_sched_group, sched_group_cpus); static int -cpu_to_cpu_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, - cpumask_t *unused) +cpu_to_cpu_group(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, struct cpumask *unused) { if (sg) *sg = &per_cpu(sched_group_cpus, cpu).sg; @@ -7100,22 +7103,21 @@ static DEFINE_PER_CPU(struct static_sched_group, sched_group_core); #if defined(CONFIG_SCHED_MC) && defined(CONFIG_SCHED_SMT) static int -cpu_to_core_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, - cpumask_t *mask) +cpu_to_core_group(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, struct cpumask *mask) { int group; - *mask = per_cpu(cpu_sibling_map, cpu); - cpus_and(*mask, *mask, *cpu_map); - group = first_cpu(*mask); + cpumask_and(mask, &per_cpu(cpu_sibling_map, cpu), cpu_map); + group = cpumask_first(mask); if (sg) *sg = &per_cpu(sched_group_core, group).sg; return group; } #elif defined(CONFIG_SCHED_MC) static int -cpu_to_core_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, - cpumask_t *unused) +cpu_to_core_group(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, struct cpumask *unused) { if (sg) *sg = &per_cpu(sched_group_core, cpu).sg; @@ -7127,18 +7129,18 @@ static DEFINE_PER_CPU(struct static_sched_domain, phys_domains); static DEFINE_PER_CPU(struct static_sched_group, sched_group_phys); static int -cpu_to_phys_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg, - cpumask_t *mask) +cpu_to_phys_group(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, struct cpumask *mask) { int group; #ifdef CONFIG_SCHED_MC + /* FIXME: Use cpu_coregroup_mask. */ *mask = cpu_coregroup_map(cpu); cpus_and(*mask, *mask, *cpu_map); - group = first_cpu(*mask); + group = cpumask_first(mask); #elif defined(CONFIG_SCHED_SMT) - *mask = per_cpu(cpu_sibling_map, cpu); - cpus_and(*mask, *mask, *cpu_map); - group = first_cpu(*mask); + cpumask_and(mask, &per_cpu(cpu_sibling_map, cpu), cpu_map); + group = cpumask_first(mask); #else group = cpu; #endif @@ -7159,14 +7161,16 @@ static struct sched_group ***sched_group_nodes_bycpu; static DEFINE_PER_CPU(struct sched_domain, allnodes_domains); static DEFINE_PER_CPU(struct static_sched_group, sched_group_allnodes); -static int cpu_to_allnodes_group(int cpu, const cpumask_t *cpu_map, - struct sched_group **sg, cpumask_t *nodemask) +static int cpu_to_allnodes_group(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, + struct cpumask *nodemask) { int group; + /* FIXME: use cpumask_of_node */ node_to_cpumask_ptr(pnodemask, cpu_to_node(cpu)); - cpus_and(*nodemask, *pnodemask, *cpu_map); - group = first_cpu(*nodemask); + cpumask_and(nodemask, pnodemask, cpu_map); + group = cpumask_first(nodemask); if (sg) *sg = &per_cpu(sched_group_allnodes, group).sg; @@ -7202,7 +7206,8 @@ static void init_numa_sched_groups_power(struct sched_group *group_head) #ifdef CONFIG_NUMA /* Free memory allocated for various sched_group structures */ -static void free_sched_groups(const cpumask_t *cpu_map, cpumask_t *nodemask) +static void free_sched_groups(const struct cpumask *cpu_map, + struct cpumask *nodemask) { int cpu, i; @@ -7215,10 +7220,11 @@ static void free_sched_groups(const cpumask_t *cpu_map, cpumask_t *nodemask) for (i = 0; i < nr_node_ids; i++) { struct sched_group *oldsg, *sg = sched_group_nodes[i]; + /* FIXME: Use cpumask_of_node */ node_to_cpumask_ptr(pnodemask, i); cpus_and(*nodemask, *pnodemask, *cpu_map); - if (cpus_empty(*nodemask)) + if (cpumask_empty(nodemask)) continue; if (sg == NULL) @@ -7236,7 +7242,8 @@ next_sg: } } #else /* !CONFIG_NUMA */ -static void free_sched_groups(const cpumask_t *cpu_map, cpumask_t *nodemask) +static void free_sched_groups(const struct cpumask *cpu_map, + struct cpumask *nodemask) { } #endif /* CONFIG_NUMA */ @@ -7366,7 +7373,7 @@ static void set_domain_attribute(struct sched_domain *sd, * Build sched domains for a given set of cpus and attach the sched domains * to the individual cpus */ -static int __build_sched_domains(const cpumask_t *cpu_map, +static int __build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *attr) { int i, err = -ENOMEM; @@ -7416,7 +7423,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, } #ifdef CONFIG_NUMA - sched_group_nodes_bycpu[first_cpu(*cpu_map)] = sched_group_nodes; + sched_group_nodes_bycpu[cpumask_first(cpu_map)] = sched_group_nodes; #endif /* @@ -7425,12 +7432,13 @@ static int __build_sched_domains(const cpumask_t *cpu_map, for_each_cpu(i, cpu_map) { struct sched_domain *sd = NULL, *p; + /* FIXME: use cpumask_of_node */ *nodemask = node_to_cpumask(cpu_to_node(i)); cpus_and(*nodemask, *nodemask, *cpu_map); #ifdef CONFIG_NUMA - if (cpus_weight(*cpu_map) > - SD_NODES_PER_DOMAIN*cpus_weight(*nodemask)) { + if (cpumask_weight(cpu_map) > + SD_NODES_PER_DOMAIN*cpumask_weight(nodemask)) { sd = &per_cpu(allnodes_domains, i); SD_INIT(sd, ALLNODES); set_domain_attribute(sd, attr); @@ -7491,9 +7499,9 @@ static int __build_sched_domains(const cpumask_t *cpu_map, #ifdef CONFIG_SCHED_SMT /* Set up CPU (sibling) groups */ for_each_cpu(i, cpu_map) { - *this_sibling_map = per_cpu(cpu_sibling_map, i); - cpus_and(*this_sibling_map, *this_sibling_map, *cpu_map); - if (i != first_cpu(*this_sibling_map)) + cpumask_and(this_sibling_map, + &per_cpu(cpu_sibling_map, i), cpu_map); + if (i != cpumask_first(this_sibling_map)) continue; init_sched_build_groups(this_sibling_map, cpu_map, @@ -7505,9 +7513,10 @@ static int __build_sched_domains(const cpumask_t *cpu_map, #ifdef CONFIG_SCHED_MC /* Set up multi-core groups */ for_each_cpu(i, cpu_map) { + /* FIXME: Use cpu_coregroup_mask */ *this_core_map = cpu_coregroup_map(i); cpus_and(*this_core_map, *this_core_map, *cpu_map); - if (i != first_cpu(*this_core_map)) + if (i != cpumask_first(this_core_map)) continue; init_sched_build_groups(this_core_map, cpu_map, @@ -7518,9 +7527,10 @@ static int __build_sched_domains(const cpumask_t *cpu_map, /* Set up physical groups */ for (i = 0; i < nr_node_ids; i++) { + /* FIXME: Use cpumask_of_node */ *nodemask = node_to_cpumask(i); cpus_and(*nodemask, *nodemask, *cpu_map); - if (cpus_empty(*nodemask)) + if (cpumask_empty(nodemask)) continue; init_sched_build_groups(nodemask, cpu_map, @@ -7541,17 +7551,18 @@ static int __build_sched_domains(const cpumask_t *cpu_map, struct sched_group *sg, *prev; int j; + /* FIXME: Use cpumask_of_node */ *nodemask = node_to_cpumask(i); - cpus_clear(*covered); + cpumask_clear(covered); cpus_and(*nodemask, *nodemask, *cpu_map); - if (cpus_empty(*nodemask)) { + if (cpumask_empty(nodemask)) { sched_group_nodes[i] = NULL; continue; } sched_domain_node_span(i, domainspan); - cpus_and(*domainspan, *domainspan, *cpu_map); + cpumask_and(domainspan, domainspan, cpu_map); sg = kmalloc_node(sizeof(struct sched_group) + cpumask_size(), GFP_KERNEL, i); @@ -7570,21 +7581,22 @@ static int __build_sched_domains(const cpumask_t *cpu_map, sg->__cpu_power = 0; cpumask_copy(sched_group_cpus(sg), nodemask); sg->next = sg; - cpus_or(*covered, *covered, *nodemask); + cpumask_or(covered, covered, nodemask); prev = sg; for (j = 0; j < nr_node_ids; j++) { int n = (i + j) % nr_node_ids; + /* FIXME: Use cpumask_of_node */ node_to_cpumask_ptr(pnodemask, n); - cpus_complement(*notcovered, *covered); - cpus_and(*tmpmask, *notcovered, *cpu_map); - cpus_and(*tmpmask, *tmpmask, *domainspan); - if (cpus_empty(*tmpmask)) + cpumask_complement(notcovered, covered); + cpumask_and(tmpmask, notcovered, cpu_map); + cpumask_and(tmpmask, tmpmask, domainspan); + if (cpumask_empty(tmpmask)) break; - cpus_and(*tmpmask, *tmpmask, *pnodemask); - if (cpus_empty(*tmpmask)) + cpumask_and(tmpmask, tmpmask, pnodemask); + if (cpumask_empty(tmpmask)) continue; sg = kmalloc_node(sizeof(struct sched_group) + @@ -7598,7 +7610,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, sg->__cpu_power = 0; cpumask_copy(sched_group_cpus(sg), tmpmask); sg->next = prev->next; - cpus_or(*covered, *covered, *tmpmask); + cpumask_or(covered, covered, tmpmask); prev->next = sg; prev = sg; } @@ -7634,7 +7646,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map, if (sd_allnodes) { struct sched_group *sg; - cpu_to_allnodes_group(first_cpu(*cpu_map), cpu_map, &sg, + cpu_to_allnodes_group(cpumask_first(cpu_map), cpu_map, &sg, tmpmask); init_numa_sched_groups_power(sg); } @@ -7690,12 +7702,12 @@ error: #endif } -static int build_sched_domains(const cpumask_t *cpu_map) +static int build_sched_domains(const struct cpumask *cpu_map) { return __build_sched_domains(cpu_map, NULL); } -static cpumask_t *doms_cur; /* current sched domains */ +static struct cpumask *doms_cur; /* current sched domains */ static int ndoms_cur; /* number of sched domains in 'doms_cur' */ static struct sched_domain_attr *dattr_cur; /* attribues of custom domains in 'doms_cur' */ @@ -7716,13 +7728,13 @@ void __attribute__((weak)) arch_update_cpu_topology(void) * For now this just excludes isolated cpus, but could be used to * exclude other special cases in the future. */ -static int arch_init_sched_domains(const cpumask_t *cpu_map) +static int arch_init_sched_domains(const struct cpumask *cpu_map) { int err; arch_update_cpu_topology(); ndoms_cur = 1; - doms_cur = kmalloc(sizeof(cpumask_t), GFP_KERNEL); + doms_cur = kmalloc(cpumask_size(), GFP_KERNEL); if (!doms_cur) doms_cur = fallback_doms; cpumask_andnot(doms_cur, cpu_map, cpu_isolated_map); @@ -7733,8 +7745,8 @@ static int arch_init_sched_domains(const cpumask_t *cpu_map) return err; } -static void arch_destroy_sched_domains(const cpumask_t *cpu_map, - cpumask_t *tmpmask) +static void arch_destroy_sched_domains(const struct cpumask *cpu_map, + struct cpumask *tmpmask) { free_sched_groups(cpu_map, tmpmask); } @@ -7743,15 +7755,16 @@ static void arch_destroy_sched_domains(const cpumask_t *cpu_map, * Detach sched domains from a group of cpus specified in cpu_map * These cpus will now be attached to the NULL domain */ -static void detach_destroy_domains(const cpumask_t *cpu_map) +static void detach_destroy_domains(const struct cpumask *cpu_map) { - cpumask_t tmpmask; + /* Save because hotplug lock held. */ + static DECLARE_BITMAP(tmpmask, CONFIG_NR_CPUS); int i; for_each_cpu(i, cpu_map) cpu_attach_domain(NULL, &def_root_domain, i); synchronize_sched(); - arch_destroy_sched_domains(cpu_map, &tmpmask); + arch_destroy_sched_domains(cpu_map, to_cpumask(tmpmask)); } /* handle null as "default" */ @@ -7776,7 +7789,7 @@ static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur, * doms_new[] to the current sched domain partitioning, doms_cur[]. * It destroys each deleted domain and builds each new domain. * - * 'doms_new' is an array of cpumask_t's of length 'ndoms_new'. + * 'doms_new' is an array of cpumask's of length 'ndoms_new'. * The masks don't intersect (don't overlap.) We should setup one * sched domain for each mask. CPUs not in any of the cpumasks will * not be load balanced. If the same cpumask appears both in the @@ -7790,13 +7803,14 @@ static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur, * the single partition 'fallback_doms', it also forces the domains * to be rebuilt. * - * If doms_new == NULL it will be replaced with cpu_online_map. + * If doms_new == NULL it will be replaced with cpu_online_mask. * ndoms_new == 0 is a special case for destroying existing domains, * and it will not create the default domain. * * Call with hotplug lock held */ -void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, +/* FIXME: Change to struct cpumask *doms_new[] */ +void partition_sched_domains(int ndoms_new, struct cpumask *doms_new, struct sched_domain_attr *dattr_new) { int i, j, n; @@ -7811,7 +7825,7 @@ void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, /* Destroy deleted domains */ for (i = 0; i < ndoms_cur; i++) { for (j = 0; j < n; j++) { - if (cpus_equal(doms_cur[i], doms_new[j]) + if (cpumask_equal(&doms_cur[i], &doms_new[j]) && dattrs_equal(dattr_cur, i, dattr_new, j)) goto match1; } @@ -7831,7 +7845,7 @@ match1: /* Build new domains */ for (i = 0; i < ndoms_new; i++) { for (j = 0; j < ndoms_cur; j++) { - if (cpus_equal(doms_new[i], doms_cur[j]) + if (cpumask_equal(&doms_new[i], &doms_cur[j]) && dattrs_equal(dattr_new, i, dattr_cur, j)) goto match2; } diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index bba00402ed90..08ffffd4a410 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -1017,7 +1017,7 @@ static void yield_task_fair(struct rq *rq) * search starts with cpus closest then further out as needed, * so we always favor a closer, idle cpu. * Domains may include CPUs that are not usable for migration, - * hence we need to mask them out (cpu_active_map) + * hence we need to mask them out (cpu_active_mask) * * Returns the CPU we should wake onto. */ @@ -1244,7 +1244,7 @@ static int select_task_rq_fair(struct task_struct *p, int sync) } } - if (unlikely(!cpu_isset(this_cpu, p->cpus_allowed))) + if (unlikely(!cpumask_test_cpu(this_cpu, &p->cpus_allowed))) goto out; /* diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index 1f0e99d1a8ce..fb3964579a8a 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c @@ -923,7 +923,7 @@ static void deactivate_task(struct rq *rq, struct task_struct *p, int sleep); static int pick_rt_task(struct rq *rq, struct task_struct *p, int cpu) { if (!task_running(rq, p) && - (cpu < 0 || cpu_isset(cpu, p->cpus_allowed)) && + (cpu < 0 || cpumask_test_cpu(cpu, &p->cpus_allowed)) && (p->rt.nr_cpus_allowed > 1)) return 1; return 0; @@ -982,7 +982,7 @@ static inline int pick_optimal_cpu(int this_cpu, cpumask_t *mask) static int find_lowest_rq(struct task_struct *task) { struct sched_domain *sd; - cpumask_t *lowest_mask = __get_cpu_var(local_cpu_mask); + struct cpumask *lowest_mask = __get_cpu_var(local_cpu_mask); int this_cpu = smp_processor_id(); int cpu = task_cpu(task); @@ -997,7 +997,7 @@ static int find_lowest_rq(struct task_struct *task) * I guess we might want to change cpupri_find() to ignore those * in the first place. */ - cpus_and(*lowest_mask, *lowest_mask, cpu_active_map); + cpumask_and(lowest_mask, lowest_mask, cpu_active_mask); /* * At this point we have built a mask of cpus representing the @@ -1007,7 +1007,7 @@ static int find_lowest_rq(struct task_struct *task) * We prioritize the last cpu that the task executed on since * it is most likely cache-hot in that location. */ - if (cpu_isset(cpu, *lowest_mask)) + if (cpumask_test_cpu(cpu, lowest_mask)) return cpu; /* @@ -1064,8 +1064,8 @@ static struct rq *find_lock_lowest_rq(struct task_struct *task, struct rq *rq) * Also make sure that it wasn't scheduled on its rq. */ if (unlikely(task_rq(task) != rq || - !cpu_isset(lowest_rq->cpu, - task->cpus_allowed) || + !cpumask_test_cpu(lowest_rq->cpu, + &task->cpus_allowed) || task_running(rq, task) || !task->se.on_rq)) { @@ -1315,9 +1315,9 @@ move_one_task_rt(struct rq *this_rq, int this_cpu, struct rq *busiest, } static void set_cpus_allowed_rt(struct task_struct *p, - const cpumask_t *new_mask) + const struct cpumask *new_mask) { - int weight = cpus_weight(*new_mask); + int weight = cpumask_weight(new_mask); BUG_ON(!rt_task(p)); @@ -1338,7 +1338,7 @@ static void set_cpus_allowed_rt(struct task_struct *p, update_rt_migration(rq); } - p->cpus_allowed = *new_mask; + cpumask_copy(&p->cpus_allowed, new_mask); p->rt.nr_cpus_allowed = weight; } -- cgit v1.2.3 From dcc7461eef7341e84e2f7274f904ce01a43b2506 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Wed, 26 Nov 2008 13:36:59 +0000 Subject: wusb: add debug files for ASL, PZL and DI to the whci-hcd driver Add asl, pzl and di debugfs files to uwb/uwbN/wusbhc for WHCI host controller. These dump the current ASL, PZL and DI buffer. Signed-off-by: David Vrabel --- drivers/usb/host/whci/Kbuild | 1 + drivers/usb/host/whci/asl.c | 25 ------ drivers/usb/host/whci/debug.c | 189 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/whci/hcd.c | 3 + drivers/usb/host/whci/pzl.c | 28 ------- drivers/usb/host/whci/qset.c | 40 --------- drivers/usb/host/whci/whcd.h | 9 +- drivers/usb/host/whci/wusb.c | 27 ------ drivers/uwb/pal.c | 5 ++ drivers/uwb/uwb-debug.c | 13 +++ drivers/uwb/uwb-internal.h | 3 +- include/linux/uwb.h | 3 + 12 files changed, 223 insertions(+), 123 deletions(-) create mode 100644 drivers/usb/host/whci/debug.c (limited to 'include/linux') diff --git a/drivers/usb/host/whci/Kbuild b/drivers/usb/host/whci/Kbuild index 26a3871ea0f9..11e5040b8337 100644 --- a/drivers/usb/host/whci/Kbuild +++ b/drivers/usb/host/whci/Kbuild @@ -2,6 +2,7 @@ obj-$(CONFIG_USB_WHCI_HCD) += whci-hcd.o whci-hcd-y := \ asl.o \ + debug.o \ hcd.o \ hw.o \ init.o \ diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index ba99a7a3f81a..577c0d29849d 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c @@ -19,32 +19,11 @@ #include #include #include -#define D_LOCAL 0 -#include #include "../../wusbcore/wusbhc.h" #include "whcd.h" -#if D_LOCAL >= 4 -static void dump_asl(struct whc *whc, const char *tag) -{ - struct device *dev = &whc->umc->dev; - struct whc_qset *qset; - - d_printf(4, dev, "ASL %s\n", tag); - - list_for_each_entry(qset, &whc->async_list, list_node) { - dump_qset(qset, dev); - } -} -#else -static inline void dump_asl(struct whc *whc, const char *tag) -{ -} -#endif - - static void qset_get_next_prev(struct whc *whc, struct whc_qset *qset, struct whc_qset **next, struct whc_qset **prev) { @@ -217,8 +196,6 @@ void scan_async_work(struct work_struct *work) spin_lock_irq(&whc->lock); - dump_asl(whc, "before processing"); - /* * Transerve the software list backwards so new qsets can be * safely inserted into the ASL without making it non-circular. @@ -232,8 +209,6 @@ void scan_async_work(struct work_struct *work) update |= process_qset(whc, qset); } - dump_asl(whc, "after processing"); - spin_unlock_irq(&whc->lock); if (update) { diff --git a/drivers/usb/host/whci/debug.c b/drivers/usb/host/whci/debug.c new file mode 100644 index 000000000000..cf2d45946c57 --- /dev/null +++ b/drivers/usb/host/whci/debug.c @@ -0,0 +1,189 @@ +/* + * Wireless Host Controller (WHC) debug. + * + * Copyright (C) 2008 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include + +#include "../../wusbcore/wusbhc.h" + +#include "whcd.h" + +struct whc_dbg { + struct dentry *di_f; + struct dentry *asl_f; + struct dentry *pzl_f; +}; + +void qset_print(struct seq_file *s, struct whc_qset *qset) +{ + struct whc_std *std; + struct urb *urb = NULL; + int i; + + seq_printf(s, "qset %08x\n", (u32)qset->qset_dma); + seq_printf(s, " -> %08x\n", (u32)qset->qh.link); + seq_printf(s, " info: %08x %08x %08x\n", + qset->qh.info1, qset->qh.info2, qset->qh.info3); + seq_printf(s, " sts: %04x errs: %d\n", qset->qh.status, qset->qh.err_count); + seq_printf(s, " TD: sts: %08x opts: %08x\n", + qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options); + + for (i = 0; i < WHCI_QSET_TD_MAX; i++) { + seq_printf(s, " %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n", + i == qset->td_start ? 'S' : ' ', + i == qset->td_end ? 'E' : ' ', + i, qset->qtd[i].status, qset->qtd[i].options, + (u32)qset->qtd[i].page_list_ptr); + } + seq_printf(s, " ntds: %d\n", qset->ntds); + list_for_each_entry(std, &qset->stds, list_node) { + if (urb != std->urb) { + urb = std->urb; + seq_printf(s, " urb %p transferred: %d bytes\n", urb, + urb->actual_length); + } + if (std->qtd) + seq_printf(s, " sTD[%td]: %zu bytes @ %08x\n", + std->qtd - &qset->qtd[0], + std->len, std->num_pointers ? + (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr); + else + seq_printf(s, " sTD[-]: %zd bytes @ %08x\n", + std->len, std->num_pointers ? + (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr); + } +} + +static int di_print(struct seq_file *s, void *p) +{ + struct whc *whc = s->private; + char buf[72]; + int d; + + for (d = 0; d < whc->n_devices; d++) { + struct di_buf_entry *di = &whc->di_buf[d]; + + bitmap_scnprintf(buf, sizeof(buf), + (unsigned long *)di->availability_info, UWB_NUM_MAS); + + seq_printf(s, "DI[%d]\n", d); + seq_printf(s, " availability: %s\n", buf); + seq_printf(s, " %c%c key idx: %d dev addr: %d\n", + (di->addr_sec_info & WHC_DI_SECURE) ? 'S' : ' ', + (di->addr_sec_info & WHC_DI_DISABLE) ? 'D' : ' ', + (di->addr_sec_info & WHC_DI_KEY_IDX_MASK) >> 8, + (di->addr_sec_info & WHC_DI_DEV_ADDR_MASK)); + } + return 0; +} + +static int asl_print(struct seq_file *s, void *p) +{ + struct whc *whc = s->private; + struct whc_qset *qset; + + list_for_each_entry(qset, &whc->async_list, list_node) { + qset_print(s, qset); + } + + return 0; +} + +static int pzl_print(struct seq_file *s, void *p) +{ + struct whc *whc = s->private; + struct whc_qset *qset; + int period; + + for (period = 0; period < 5; period++) { + seq_printf(s, "Period %d\n", period); + list_for_each_entry(qset, &whc->periodic_list[period], list_node) { + qset_print(s, qset); + } + } + return 0; +} + +static int di_open(struct inode *inode, struct file *file) +{ + return single_open(file, di_print, inode->i_private); +} + +static int asl_open(struct inode *inode, struct file *file) +{ + return single_open(file, asl_print, inode->i_private); +} + +static int pzl_open(struct inode *inode, struct file *file) +{ + return single_open(file, pzl_print, inode->i_private); +} + +static struct file_operations di_fops = { + .open = di_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct file_operations asl_fops = { + .open = asl_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct file_operations pzl_fops = { + .open = pzl_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +void whc_dbg_init(struct whc *whc) +{ + if (whc->wusbhc.pal.debugfs_dir == NULL) + return; + + whc->dbg = kzalloc(sizeof(struct whc_dbg), GFP_KERNEL); + if (whc->dbg == NULL) + return; + + whc->dbg->di_f = debugfs_create_file("di", 0444, + whc->wusbhc.pal.debugfs_dir, whc, + &di_fops); + whc->dbg->asl_f = debugfs_create_file("asl", 0444, + whc->wusbhc.pal.debugfs_dir, whc, + &asl_fops); + whc->dbg->pzl_f = debugfs_create_file("pzl", 0444, + whc->wusbhc.pal.debugfs_dir, whc, + &pzl_fops); +} + +void whc_dbg_clean_up(struct whc *whc) +{ + if (whc->dbg) { + debugfs_remove(whc->dbg->pzl_f); + debugfs_remove(whc->dbg->asl_f); + debugfs_remove(whc->dbg->di_f); + kfree(whc->dbg); + } +} diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index f599f89d3be1..1569afd6245b 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c @@ -273,6 +273,8 @@ static int whc_probe(struct umc_dev *umc) goto error_wusbhc_b_create; } + whc_dbg_init(whc); + return 0; error_wusbhc_b_create: @@ -296,6 +298,7 @@ static void whc_remove(struct umc_dev *umc) struct whc *whc = wusbhc_to_whc(wusbhc); if (usb_hcd) { + whc_dbg_clean_up(whc); wusbhc_b_destroy(wusbhc); usb_remove_hcd(usb_hcd); wusbhc_destroy(wusbhc); diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c index 34d3a0aeab2b..2ae5abf69a6a 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/usb/host/whci/pzl.c @@ -19,35 +19,11 @@ #include #include #include -#define D_LOCAL 0 -#include #include "../../wusbcore/wusbhc.h" #include "whcd.h" -#if D_LOCAL >= 4 -static void dump_pzl(struct whc *whc, const char *tag) -{ - struct device *dev = &whc->umc->dev; - struct whc_qset *qset; - int period = 0; - - d_printf(4, dev, "PZL %s\n", tag); - - for (period = 0; period < 5; period++) { - d_printf(4, dev, "Period %d\n", period); - list_for_each_entry(qset, &whc->periodic_list[period], list_node) { - dump_qset(qset, dev); - } - } -} -#else -static inline void dump_pzl(struct whc *whc, const char *tag) -{ -} -#endif - static void update_pzl_pointers(struct whc *whc, int period, u64 addr) { switch (period) { @@ -250,8 +226,6 @@ void scan_periodic_work(struct work_struct *work) spin_lock_irq(&whc->lock); - dump_pzl(whc, "before processing"); - for (period = 4; period >= 0; period--) { list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { if (!qset->in_hw_list) @@ -263,8 +237,6 @@ void scan_periodic_work(struct work_struct *work) if (update & (WHC_UPDATE_ADDED | WHC_UPDATE_REMOVED)) update_pzl_hw_view(whc); - dump_pzl(whc, "after processing"); - spin_unlock_irq(&whc->lock); if (update) { diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index 0420037d2e18..7be74314ee12 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c @@ -24,46 +24,6 @@ #include "whcd.h" -void dump_qset(struct whc_qset *qset, struct device *dev) -{ - struct whc_std *std; - struct urb *urb = NULL; - int i; - - dev_dbg(dev, "qset %08x\n", (u32)qset->qset_dma); - dev_dbg(dev, " -> %08x\n", (u32)qset->qh.link); - dev_dbg(dev, " info: %08x %08x %08x\n", - qset->qh.info1, qset->qh.info2, qset->qh.info3); - dev_dbg(dev, " sts: %04x errs: %d\n", qset->qh.status, qset->qh.err_count); - dev_dbg(dev, " TD: sts: %08x opts: %08x\n", - qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options); - - for (i = 0; i < WHCI_QSET_TD_MAX; i++) { - dev_dbg(dev, " %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n", - i == qset->td_start ? 'S' : ' ', - i == qset->td_end ? 'E' : ' ', - i, qset->qtd[i].status, qset->qtd[i].options, - (u32)qset->qtd[i].page_list_ptr); - } - dev_dbg(dev, " ntds: %d\n", qset->ntds); - list_for_each_entry(std, &qset->stds, list_node) { - if (urb != std->urb) { - urb = std->urb; - dev_dbg(dev, " urb %p transferred: %d bytes\n", urb, - urb->actual_length); - } - if (std->qtd) - dev_dbg(dev, " sTD[%td]: %zu bytes @ %08x\n", - std->qtd - &qset->qtd[0], - std->len, std->num_pointers ? - (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr); - else - dev_dbg(dev, " sTD[-]: %zd bytes @ %08x\n", - std->len, std->num_pointers ? - (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr); - } -} - struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags) { struct whc_qset *qset; diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h index 1bbb8cb6bf80..0f3540f04f53 100644 --- a/drivers/usb/host/whci/whcd.h +++ b/drivers/usb/host/whci/whcd.h @@ -21,6 +21,7 @@ #define __WHCD_H #include +#include #include #include "whci-hc.h" @@ -28,6 +29,7 @@ /* Generic command timeout. */ #define WHC_GENCMD_TIMEOUT_MS 100 +struct whc_dbg; struct whc { struct wusbhc wusbhc; @@ -69,6 +71,8 @@ struct whc { struct list_head periodic_removed_list; wait_queue_head_t periodic_list_wq; struct work_struct periodic_work; + + struct whc_dbg *dbg; }; #define wusbhc_to_whc(w) (container_of((w), struct whc, wusbhc)) @@ -190,8 +194,11 @@ void process_inactive_qtd(struct whc *whc, struct whc_qset *qset, struct whc_qtd *qtd); enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset); void qset_remove_complete(struct whc *whc, struct whc_qset *qset); -void dump_qset(struct whc_qset *qset, struct device *dev); void pzl_update(struct whc *whc, uint32_t wusbcmd); void asl_update(struct whc *whc, uint32_t wusbcmd); +/* debug.c */ +void whc_dbg_init(struct whc *whc); +void whc_dbg_clean_up(struct whc *whc); + #endif /* #ifndef __WHCD_H */ diff --git a/drivers/usb/host/whci/wusb.c b/drivers/usb/host/whci/wusb.c index 540021a0971e..f24efdebad17 100644 --- a/drivers/usb/host/whci/wusb.c +++ b/drivers/usb/host/whci/wusb.c @@ -18,43 +18,16 @@ #include #include #include -#define D_LOCAL 1 -#include #include "../../wusbcore/wusbhc.h" #include "whcd.h" -#if D_LOCAL >= 1 -static void dump_di(struct whc *whc, int idx) -{ - struct di_buf_entry *di = &whc->di_buf[idx]; - struct device *dev = &whc->umc->dev; - char buf[128]; - - bitmap_scnprintf(buf, sizeof(buf), (unsigned long *)di->availability_info, UWB_NUM_MAS); - - d_printf(1, dev, "DI[%d]\n", idx); - d_printf(1, dev, " availability: %s\n", buf); - d_printf(1, dev, " %c%c key idx: %d dev addr: %d\n", - (di->addr_sec_info & WHC_DI_SECURE) ? 'S' : ' ', - (di->addr_sec_info & WHC_DI_DISABLE) ? 'D' : ' ', - (di->addr_sec_info & WHC_DI_KEY_IDX_MASK) >> 8, - (di->addr_sec_info & WHC_DI_DEV_ADDR_MASK)); -} -#else -static inline void dump_di(struct whc *whc, int idx) -{ -} -#endif - static int whc_update_di(struct whc *whc, int idx) { int offset = idx / 32; u32 bit = 1 << (idx % 32); - dump_di(whc, idx); - le_writel(bit, whc->base + WUSBDIBUPDATED + offset); return whci_wait_for(&whc->umc->dev, diff --git a/drivers/uwb/pal.c b/drivers/uwb/pal.c index 605765124f5b..99a19c199095 100644 --- a/drivers/uwb/pal.c +++ b/drivers/uwb/pal.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ #include +#include #include #include "uwb-internal.h" @@ -54,6 +55,8 @@ int uwb_pal_register(struct uwb_pal *pal) } } + pal->debugfs_dir = uwb_dbg_create_pal_dir(pal); + mutex_lock(&rc->uwb_dev.mutex); list_add(&pal->node, &rc->pals); mutex_unlock(&rc->uwb_dev.mutex); @@ -76,6 +79,8 @@ void uwb_pal_unregister(struct uwb_pal *pal) list_del(&pal->node); mutex_unlock(&rc->uwb_dev.mutex); + debugfs_remove(pal->debugfs_dir); + if (pal->device) { sysfs_remove_link(&rc->uwb_dev.dev.kobj, pal->name); sysfs_remove_link(&pal->device->kobj, "uwb_rc"); diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c index ec1b7a403ff3..a6debb9baf38 100644 --- a/drivers/uwb/uwb-debug.c +++ b/drivers/uwb/uwb-debug.c @@ -407,3 +407,16 @@ void uwb_dbg_exit(void) { debugfs_remove(root_dir); } + +/** + * uwb_dbg_create_pal_dir - create a debugfs directory for a PAL + * @pal: The PAL. + */ +struct dentry *uwb_dbg_create_pal_dir(struct uwb_pal *pal) +{ + struct uwb_rc *rc = pal->rc; + + if (root_dir && rc->dbg && rc->dbg->root_d && pal->name) + return debugfs_create_dir(pal->name, rc->dbg->root_d); + return NULL; +} diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h index 9c0cdb4ded0c..f0f21f406bf0 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/uwb/uwb-internal.h @@ -284,8 +284,7 @@ void uwb_dbg_init(void); void uwb_dbg_exit(void); void uwb_dbg_add_rc(struct uwb_rc *rc); void uwb_dbg_del_rc(struct uwb_rc *rc); - -/* Workarounds for version specific stuff */ +struct dentry *uwb_dbg_create_pal_dir(struct uwb_pal *pal); static inline void uwb_dev_lock(struct uwb_dev *uwb_dev) { diff --git a/include/linux/uwb.h b/include/linux/uwb.h index 1719709d60ca..d7ed5201ade6 100644 --- a/include/linux/uwb.h +++ b/include/linux/uwb.h @@ -394,6 +394,8 @@ struct uwb_rc { * @channel: channel being used by the PAL; 0 if the PAL isn't using * the radio; -1 if the PAL wishes to use the radio but * cannot. + * @debugfs_dir: a debugfs directory which the PAL can use for its own + * debugfs files. * * A Protocol Adaptation Layer (PAL) is a user of the WiMedia UWB * radio platform (e.g., WUSB, WLP or Bluetooth UWB AMP). @@ -418,6 +420,7 @@ struct uwb_pal { void (*new_rsv)(struct uwb_pal *pal, struct uwb_rsv *rsv); int channel; + struct dentry *debugfs_dir; }; void uwb_pal_init(struct uwb_pal *pal); -- cgit v1.2.3 From 5b37717a23b8e40f6cf7ad85a26ddcf41c171e2c Mon Sep 17 00:00:00 2001 From: Stefano Panella Date: Fri, 12 Dec 2008 13:00:06 +0000 Subject: uwb: improved MAS allocator and reservation conflict handling Greatly enhance the MAS allocator: - Handle row and column reservations. - Permit all the available MAS to be allocated. - Follows the WiMedia rules on MAS selection. Take appropriate action when reservation conflicts are detected. - Correctly identify which reservation wins the conflict. - Protect alien BP reservations. - If an owned reservation loses, resize/move it. - Follow the backoff procedure before requesting additional MAS. When reservations are terminated, move the remaining reservations (if necessary) so they keep following the MAS allocation rules. Signed-off-by: Stefano Panella Signed-off-by: David Vrabel --- drivers/usb/wusbcore/reservation.c | 13 +- drivers/uwb/Makefile | 1 + drivers/uwb/allocator.c | 386 +++++++++++++++++++++ drivers/uwb/drp-avail.c | 4 +- drivers/uwb/drp-ie.c | 160 +++++++-- drivers/uwb/drp.c | 681 ++++++++++++++++++++++++++++--------- drivers/uwb/rsv.c | 482 ++++++++++++++++++++------ drivers/uwb/uwb-debug.c | 49 +-- drivers/uwb/uwb-internal.h | 80 ++++- include/linux/uwb.h | 47 ++- include/linux/uwb/debug-cmd.h | 2 +- include/linux/uwb/spec.h | 25 ++ 12 files changed, 1603 insertions(+), 327 deletions(-) create mode 100644 drivers/uwb/allocator.c (limited to 'include/linux') diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/usb/wusbcore/reservation.c index 7b6525dac2f1..c37e4f83e54a 100644 --- a/drivers/usb/wusbcore/reservation.c +++ b/drivers/usb/wusbcore/reservation.c @@ -48,13 +48,15 @@ static void wusbhc_rsv_complete_cb(struct uwb_rsv *rsv) { struct wusbhc *wusbhc = rsv->pal_priv; struct device *dev = wusbhc->dev; + struct uwb_mas_bm mas; char buf[72]; switch (rsv->state) { case UWB_RSV_STATE_O_ESTABLISHED: - bitmap_scnprintf(buf, sizeof(buf), rsv->mas.bm, UWB_NUM_MAS); + uwb_rsv_get_usable_mas(rsv, &mas); + bitmap_scnprintf(buf, sizeof(buf), mas.bm, UWB_NUM_MAS); dev_dbg(dev, "established reservation: %s\n", buf); - wusbhc_bwa_set(wusbhc, rsv->stream, &rsv->mas); + wusbhc_bwa_set(wusbhc, rsv->stream, &mas); break; case UWB_RSV_STATE_NONE: dev_dbg(dev, "removed reservation\n"); @@ -85,13 +87,12 @@ int wusbhc_rsv_establish(struct wusbhc *wusbhc) bcid.data[0] = wusbhc->cluster_id; bcid.data[1] = 0; - rsv->owner = &rc->uwb_dev; rsv->target.type = UWB_RSV_TARGET_DEVADDR; rsv->target.devaddr = bcid; rsv->type = UWB_DRP_TYPE_PRIVATE; - rsv->max_mas = 256; - rsv->min_mas = 16; /* one MAS per zone? */ - rsv->sparsity = 16; /* at least one MAS in each zone? */ + rsv->max_mas = 256; /* try to get as much as possible */ + rsv->min_mas = 15; /* one MAS per zone */ + rsv->max_interval = 1; /* max latency is one zone */ rsv->is_multicast = true; ret = uwb_rsv_establish(rsv); diff --git a/drivers/uwb/Makefile b/drivers/uwb/Makefile index ce21a95da04a..2f98d080fe78 100644 --- a/drivers/uwb/Makefile +++ b/drivers/uwb/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_UWB_I1480U) += i1480/ uwb-objs := \ address.o \ + allocator.o \ beacon.o \ driver.o \ drp.o \ diff --git a/drivers/uwb/allocator.c b/drivers/uwb/allocator.c new file mode 100644 index 000000000000..c8185e6b0cd5 --- /dev/null +++ b/drivers/uwb/allocator.c @@ -0,0 +1,386 @@ +/* + * UWB reservation management. + * + * Copyright (C) 2008 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include + +#include "uwb-internal.h" + +static void uwb_rsv_fill_column_alloc(struct uwb_rsv_alloc_info *ai) +{ + int col, mas, safe_mas, unsafe_mas; + unsigned char *bm = ai->bm; + struct uwb_rsv_col_info *ci = ai->ci; + unsigned char c; + + for (col = ci->csi.start_col; col < UWB_NUM_ZONES; col += ci->csi.interval) { + + safe_mas = ci->csi.safe_mas_per_col; + unsafe_mas = ci->csi.unsafe_mas_per_col; + + for (mas = 0; mas < UWB_MAS_PER_ZONE; mas++ ) { + if (bm[col * UWB_MAS_PER_ZONE + mas] == 0) { + + if (safe_mas > 0) { + safe_mas--; + c = UWB_RSV_MAS_SAFE; + } else if (unsafe_mas > 0) { + unsafe_mas--; + c = UWB_RSV_MAS_UNSAFE; + } else { + break; + } + bm[col * UWB_MAS_PER_ZONE + mas] = c; + } + } + } +} + +static void uwb_rsv_fill_row_alloc(struct uwb_rsv_alloc_info *ai) +{ + int mas, col, rows; + unsigned char *bm = ai->bm; + struct uwb_rsv_row_info *ri = &ai->ri; + unsigned char c; + + rows = 1; + c = UWB_RSV_MAS_SAFE; + for (mas = UWB_MAS_PER_ZONE - 1; mas >= 0; mas--) { + if (ri->avail[mas] == 1) { + + if (rows > ri->used_rows) { + break; + } else if (rows > 7) { + c = UWB_RSV_MAS_UNSAFE; + } + + for (col = 0; col < UWB_NUM_ZONES; col++) { + if (bm[col * UWB_NUM_ZONES + mas] != UWB_RSV_MAS_NOT_AVAIL) { + bm[col * UWB_NUM_ZONES + mas] = c; + if(c == UWB_RSV_MAS_SAFE) + ai->safe_allocated_mases++; + else + ai->unsafe_allocated_mases++; + } + } + rows++; + } + } + ai->total_allocated_mases = ai->safe_allocated_mases + ai->unsafe_allocated_mases; +} + +/* + * Find the best column set for a given availability, interval, num safe mas and + * num unsafe mas. + * + * The different sets are tried in order as shown below, depending on the interval. + * + * interval = 16 + * deep = 0 + * set 1 -> { 8 } + * deep = 1 + * set 1 -> { 4 } + * set 2 -> { 12 } + * deep = 2 + * set 1 -> { 2 } + * set 2 -> { 6 } + * set 3 -> { 10 } + * set 4 -> { 14 } + * deep = 3 + * set 1 -> { 1 } + * set 2 -> { 3 } + * set 3 -> { 5 } + * set 4 -> { 7 } + * set 5 -> { 9 } + * set 6 -> { 11 } + * set 7 -> { 13 } + * set 8 -> { 15 } + * + * interval = 8 + * deep = 0 + * set 1 -> { 4 12 } + * deep = 1 + * set 1 -> { 2 10 } + * set 2 -> { 6 14 } + * deep = 2 + * set 1 -> { 1 9 } + * set 2 -> { 3 11 } + * set 3 -> { 5 13 } + * set 4 -> { 7 15 } + * + * interval = 4 + * deep = 0 + * set 1 -> { 2 6 10 14 } + * deep = 1 + * set 1 -> { 1 5 9 13 } + * set 2 -> { 3 7 11 15 } + * + * interval = 2 + * deep = 0 + * set 1 -> { 1 3 5 7 9 11 13 15 } + */ +static int uwb_rsv_find_best_column_set(struct uwb_rsv_alloc_info *ai, int interval, + int num_safe_mas, int num_unsafe_mas) +{ + struct uwb_rsv_col_info *ci = ai->ci; + struct uwb_rsv_col_set_info *csi = &ci->csi; + struct uwb_rsv_col_set_info tmp_csi; + int deep, set, col, start_col_deep, col_start_set; + int start_col, max_mas_in_set, lowest_max_mas_in_deep; + int n_mas; + int found = UWB_RSV_ALLOC_NOT_FOUND; + + tmp_csi.start_col = 0; + start_col_deep = interval; + n_mas = num_unsafe_mas + num_safe_mas; + + for (deep = 0; ((interval >> deep) & 0x1) == 0; deep++) { + start_col_deep /= 2; + col_start_set = 0; + lowest_max_mas_in_deep = UWB_MAS_PER_ZONE; + + for (set = 1; set <= (1 << deep); set++) { + max_mas_in_set = 0; + start_col = start_col_deep + col_start_set; + for (col = start_col; col < UWB_NUM_ZONES; col += interval) { + + if (ci[col].max_avail_safe >= num_safe_mas && + ci[col].max_avail_unsafe >= n_mas) { + if (ci[col].highest_mas[n_mas] > max_mas_in_set) + max_mas_in_set = ci[col].highest_mas[n_mas]; + } else { + max_mas_in_set = 0; + break; + } + } + if ((lowest_max_mas_in_deep > max_mas_in_set) && max_mas_in_set) { + lowest_max_mas_in_deep = max_mas_in_set; + + tmp_csi.start_col = start_col; + } + col_start_set += (interval >> deep); + } + + if (lowest_max_mas_in_deep < 8) { + csi->start_col = tmp_csi.start_col; + found = UWB_RSV_ALLOC_FOUND; + break; + } else if ((lowest_max_mas_in_deep > 8) && + (lowest_max_mas_in_deep != UWB_MAS_PER_ZONE) && + (found == UWB_RSV_ALLOC_NOT_FOUND)) { + csi->start_col = tmp_csi.start_col; + found = UWB_RSV_ALLOC_FOUND; + } + } + + if (found == UWB_RSV_ALLOC_FOUND) { + csi->interval = interval; + csi->safe_mas_per_col = num_safe_mas; + csi->unsafe_mas_per_col = num_unsafe_mas; + + ai->safe_allocated_mases = (UWB_NUM_ZONES / interval) * num_safe_mas; + ai->unsafe_allocated_mases = (UWB_NUM_ZONES / interval) * num_unsafe_mas; + ai->total_allocated_mases = ai->safe_allocated_mases + ai->unsafe_allocated_mases; + ai->interval = interval; + } + return found; +} + +static void get_row_descriptors(struct uwb_rsv_alloc_info *ai) +{ + unsigned char *bm = ai->bm; + struct uwb_rsv_row_info *ri = &ai->ri; + int col, mas; + + ri->free_rows = 16; + for (mas = 0; mas < UWB_MAS_PER_ZONE; mas ++) { + ri->avail[mas] = 1; + for (col = 1; col < UWB_NUM_ZONES; col++) { + if (bm[col * UWB_NUM_ZONES + mas] == UWB_RSV_MAS_NOT_AVAIL) { + ri->free_rows--; + ri->avail[mas]=0; + break; + } + } + } +} + +static void uwb_rsv_fill_column_info(unsigned char *bm, int column, struct uwb_rsv_col_info *rci) +{ + int mas; + int block_count = 0, start_block = 0; + int previous_avail = 0; + int available = 0; + int safe_mas_in_row[UWB_MAS_PER_ZONE] = { + 8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, + }; + + rci->max_avail_safe = 0; + + for (mas = 0; mas < UWB_MAS_PER_ZONE; mas ++) { + if (!bm[column * UWB_NUM_ZONES + mas]) { + available++; + rci->max_avail_unsafe = available; + + rci->highest_mas[available] = mas; + + if (previous_avail) { + block_count++; + if ((block_count > safe_mas_in_row[start_block]) && + (!rci->max_avail_safe)) + rci->max_avail_safe = available - 1; + } else { + previous_avail = 1; + start_block = mas; + block_count = 1; + } + } else { + previous_avail = 0; + } + } + if (!rci->max_avail_safe) + rci->max_avail_safe = rci->max_avail_unsafe; +} + +static void get_column_descriptors(struct uwb_rsv_alloc_info *ai) +{ + unsigned char *bm = ai->bm; + struct uwb_rsv_col_info *ci = ai->ci; + int col; + + for (col = 1; col < UWB_NUM_ZONES; col++) { + uwb_rsv_fill_column_info(bm, col, &ci[col]); + } +} + +static int uwb_rsv_find_best_row_alloc(struct uwb_rsv_alloc_info *ai) +{ + int n_rows; + int max_rows = ai->max_mas / UWB_USABLE_MAS_PER_ROW; + int min_rows = ai->min_mas / UWB_USABLE_MAS_PER_ROW; + if (ai->min_mas % UWB_USABLE_MAS_PER_ROW) + min_rows++; + for (n_rows = max_rows; n_rows >= min_rows; n_rows--) { + if (n_rows <= ai->ri.free_rows) { + ai->ri.used_rows = n_rows; + ai->interval = 1; /* row reservation */ + uwb_rsv_fill_row_alloc(ai); + return UWB_RSV_ALLOC_FOUND; + } + } + return UWB_RSV_ALLOC_NOT_FOUND; +} + +static int uwb_rsv_find_best_col_alloc(struct uwb_rsv_alloc_info *ai, int interval) +{ + int n_safe, n_unsafe, n_mas; + int n_column = UWB_NUM_ZONES / interval; + int max_per_zone = ai->max_mas / n_column; + int min_per_zone = ai->min_mas / n_column; + + if (ai->min_mas % n_column) + min_per_zone++; + + if (min_per_zone > UWB_MAS_PER_ZONE) { + return UWB_RSV_ALLOC_NOT_FOUND; + } + + if (max_per_zone > UWB_MAS_PER_ZONE) { + max_per_zone = UWB_MAS_PER_ZONE; + } + + for (n_mas = max_per_zone; n_mas >= min_per_zone; n_mas--) { + if (uwb_rsv_find_best_column_set(ai, interval, 0, n_mas) == UWB_RSV_ALLOC_NOT_FOUND) + continue; + for (n_safe = n_mas; n_safe >= 0; n_safe--) { + n_unsafe = n_mas - n_safe; + if (uwb_rsv_find_best_column_set(ai, interval, n_safe, n_unsafe) == UWB_RSV_ALLOC_FOUND) { + uwb_rsv_fill_column_alloc(ai); + return UWB_RSV_ALLOC_FOUND; + } + } + } + return UWB_RSV_ALLOC_NOT_FOUND; +} + +int uwb_rsv_find_best_allocation(struct uwb_rsv *rsv, struct uwb_mas_bm *available, + struct uwb_mas_bm *result) +{ + struct uwb_rsv_alloc_info *ai; + int interval; + int bit_index; + + ai = kzalloc(sizeof(struct uwb_rsv_alloc_info), GFP_KERNEL); + + ai->min_mas = rsv->min_mas; + ai->max_mas = rsv->max_mas; + ai->max_interval = rsv->max_interval; + + + /* fill the not available vector from the available bm */ + for (bit_index = 0; bit_index < UWB_NUM_MAS; bit_index++) { + if (!test_bit(bit_index, available->bm)) + ai->bm[bit_index] = UWB_RSV_MAS_NOT_AVAIL; + } + + if (ai->max_interval == 1) { + get_row_descriptors(ai); + if (uwb_rsv_find_best_row_alloc(ai) == UWB_RSV_ALLOC_FOUND) + goto alloc_found; + else + goto alloc_not_found; + } + + get_column_descriptors(ai); + + for (interval = 16; interval >= 2; interval>>=1) { + if (interval > ai->max_interval) + continue; + if (uwb_rsv_find_best_col_alloc(ai, interval) == UWB_RSV_ALLOC_FOUND) + goto alloc_found; + } + + /* try row reservation if no column is found */ + get_row_descriptors(ai); + if (uwb_rsv_find_best_row_alloc(ai) == UWB_RSV_ALLOC_FOUND) + goto alloc_found; + else + goto alloc_not_found; + + alloc_found: + bitmap_zero(result->bm, UWB_NUM_MAS); + bitmap_zero(result->unsafe_bm, UWB_NUM_MAS); + /* fill the safe and unsafe bitmaps */ + for (bit_index = 0; bit_index < UWB_NUM_MAS; bit_index++) { + if (ai->bm[bit_index] == UWB_RSV_MAS_SAFE) + set_bit(bit_index, result->bm); + else if (ai->bm[bit_index] == UWB_RSV_MAS_UNSAFE) + set_bit(bit_index, result->unsafe_bm); + } + bitmap_or(result->bm, result->bm, result->unsafe_bm, UWB_NUM_MAS); + + result->safe = ai->safe_allocated_mases; + result->unsafe = ai->unsafe_allocated_mases; + + kfree(ai); + return UWB_RSV_ALLOC_FOUND; + + alloc_not_found: + kfree(ai); + return UWB_RSV_ALLOC_NOT_FOUND; +} diff --git a/drivers/uwb/drp-avail.c b/drivers/uwb/drp-avail.c index 3febd8552808..40a540a5a72e 100644 --- a/drivers/uwb/drp-avail.c +++ b/drivers/uwb/drp-avail.c @@ -58,7 +58,7 @@ void uwb_drp_avail_init(struct uwb_rc *rc) * * avail = global & local & pending */ -static void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail) +void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail) { bitmap_and(avail->bm, rc->drp_avail.global, rc->drp_avail.local, UWB_NUM_MAS); bitmap_and(avail->bm, avail->bm, rc->drp_avail.pending, UWB_NUM_MAS); @@ -105,6 +105,7 @@ void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas) bitmap_or(rc->drp_avail.local, rc->drp_avail.local, mas->bm, UWB_NUM_MAS); bitmap_or(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS); rc->drp_avail.ie_valid = false; + uwb_rsv_handle_drp_avail_change(rc); } /** @@ -280,6 +281,7 @@ int uwbd_evt_handle_rc_drp_avail(struct uwb_event *evt) mutex_lock(&rc->rsvs_mutex); bitmap_copy(rc->drp_avail.global, bmp, UWB_NUM_MAS); rc->drp_avail.ie_valid = false; + uwb_rsv_handle_drp_avail_change(rc); mutex_unlock(&rc->rsvs_mutex); uwb_rsv_sched_update(rc); diff --git a/drivers/uwb/drp-ie.c b/drivers/uwb/drp-ie.c index 75491d47806b..2840d7bf9e67 100644 --- a/drivers/uwb/drp-ie.c +++ b/drivers/uwb/drp-ie.c @@ -22,6 +22,96 @@ #include "uwb-internal.h" + +/* + * Return the reason code for a reservations's DRP IE. + */ +int uwb_rsv_reason_code(struct uwb_rsv *rsv) +{ + static const int reason_codes[] = { + [UWB_RSV_STATE_O_INITIATED] = UWB_DRP_REASON_ACCEPTED, + [UWB_RSV_STATE_O_PENDING] = UWB_DRP_REASON_ACCEPTED, + [UWB_RSV_STATE_O_MODIFIED] = UWB_DRP_REASON_MODIFIED, + [UWB_RSV_STATE_O_ESTABLISHED] = UWB_DRP_REASON_ACCEPTED, + [UWB_RSV_STATE_O_TO_BE_MOVED] = UWB_DRP_REASON_ACCEPTED, + [UWB_RSV_STATE_O_MOVE_COMBINING] = UWB_DRP_REASON_MODIFIED, + [UWB_RSV_STATE_O_MOVE_REDUCING] = UWB_DRP_REASON_MODIFIED, + [UWB_RSV_STATE_O_MOVE_EXPANDING] = UWB_DRP_REASON_ACCEPTED, + [UWB_RSV_STATE_T_ACCEPTED] = UWB_DRP_REASON_ACCEPTED, + [UWB_RSV_STATE_T_CONFLICT] = UWB_DRP_REASON_CONFLICT, + [UWB_RSV_STATE_T_PENDING] = UWB_DRP_REASON_PENDING, + [UWB_RSV_STATE_T_DENIED] = UWB_DRP_REASON_DENIED, + [UWB_RSV_STATE_T_RESIZED] = UWB_DRP_REASON_ACCEPTED, + [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = UWB_DRP_REASON_ACCEPTED, + [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = UWB_DRP_REASON_CONFLICT, + [UWB_RSV_STATE_T_EXPANDING_PENDING] = UWB_DRP_REASON_PENDING, + [UWB_RSV_STATE_T_EXPANDING_DENIED] = UWB_DRP_REASON_DENIED, + }; + + return reason_codes[rsv->state]; +} + +/* + * Return the reason code for a reservations's companion DRP IE . + */ +int uwb_rsv_companion_reason_code(struct uwb_rsv *rsv) +{ + static const int companion_reason_codes[] = { + [UWB_RSV_STATE_O_MOVE_EXPANDING] = UWB_DRP_REASON_ACCEPTED, + [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = UWB_DRP_REASON_ACCEPTED, + [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = UWB_DRP_REASON_CONFLICT, + [UWB_RSV_STATE_T_EXPANDING_PENDING] = UWB_DRP_REASON_PENDING, + [UWB_RSV_STATE_T_EXPANDING_DENIED] = UWB_DRP_REASON_DENIED, + }; + + return companion_reason_codes[rsv->state]; +} + +/* + * Return the status bit for a reservations's DRP IE. + */ +int uwb_rsv_status(struct uwb_rsv *rsv) +{ + static const int statuses[] = { + [UWB_RSV_STATE_O_INITIATED] = 0, + [UWB_RSV_STATE_O_PENDING] = 0, + [UWB_RSV_STATE_O_MODIFIED] = 1, + [UWB_RSV_STATE_O_ESTABLISHED] = 1, + [UWB_RSV_STATE_O_TO_BE_MOVED] = 0, + [UWB_RSV_STATE_O_MOVE_COMBINING] = 1, + [UWB_RSV_STATE_O_MOVE_REDUCING] = 1, + [UWB_RSV_STATE_O_MOVE_EXPANDING] = 1, + [UWB_RSV_STATE_T_ACCEPTED] = 1, + [UWB_RSV_STATE_T_CONFLICT] = 0, + [UWB_RSV_STATE_T_PENDING] = 0, + [UWB_RSV_STATE_T_DENIED] = 0, + [UWB_RSV_STATE_T_RESIZED] = 1, + [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = 1, + [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = 1, + [UWB_RSV_STATE_T_EXPANDING_PENDING] = 1, + [UWB_RSV_STATE_T_EXPANDING_DENIED] = 1, + + }; + + return statuses[rsv->state]; +} + +/* + * Return the status bit for a reservations's companion DRP IE . + */ +int uwb_rsv_companion_status(struct uwb_rsv *rsv) +{ + static const int companion_statuses[] = { + [UWB_RSV_STATE_O_MOVE_EXPANDING] = 0, + [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = 1, + [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = 0, + [UWB_RSV_STATE_T_EXPANDING_PENDING] = 0, + [UWB_RSV_STATE_T_EXPANDING_DENIED] = 0, + }; + + return companion_statuses[rsv->state]; +} + /* * Allocate a DRP IE. * @@ -33,16 +123,12 @@ static struct uwb_ie_drp *uwb_drp_ie_alloc(void) { struct uwb_ie_drp *drp_ie; - unsigned tiebreaker; drp_ie = kzalloc(sizeof(struct uwb_ie_drp) + UWB_NUM_ZONES * sizeof(struct uwb_drp_alloc), GFP_KERNEL); if (drp_ie) { drp_ie->hdr.element_id = UWB_IE_DRP; - - get_random_bytes(&tiebreaker, sizeof(unsigned)); - uwb_ie_drp_set_tiebreaker(drp_ie, tiebreaker & 1); } return drp_ie; } @@ -103,43 +189,17 @@ static void uwb_drp_ie_from_bm(struct uwb_ie_drp *drp_ie, */ int uwb_drp_ie_update(struct uwb_rsv *rsv) { - struct device *dev = &rsv->rc->uwb_dev.dev; struct uwb_ie_drp *drp_ie; - int reason_code, status; + struct uwb_rsv_move *mv; + int unsafe; - switch (rsv->state) { - case UWB_RSV_STATE_NONE: + if (rsv->state == UWB_RSV_STATE_NONE) { kfree(rsv->drp_ie); rsv->drp_ie = NULL; return 0; - case UWB_RSV_STATE_O_INITIATED: - reason_code = UWB_DRP_REASON_ACCEPTED; - status = 0; - break; - case UWB_RSV_STATE_O_PENDING: - reason_code = UWB_DRP_REASON_ACCEPTED; - status = 0; - break; - case UWB_RSV_STATE_O_MODIFIED: - reason_code = UWB_DRP_REASON_MODIFIED; - status = 1; - break; - case UWB_RSV_STATE_O_ESTABLISHED: - reason_code = UWB_DRP_REASON_ACCEPTED; - status = 1; - break; - case UWB_RSV_STATE_T_ACCEPTED: - reason_code = UWB_DRP_REASON_ACCEPTED; - status = 1; - break; - case UWB_RSV_STATE_T_DENIED: - reason_code = UWB_DRP_REASON_DENIED; - status = 0; - break; - default: - dev_dbg(dev, "rsv with unhandled state (%d)\n", rsv->state); - return -EINVAL; } + + unsafe = rsv->mas.unsafe ? 1 : 0; if (rsv->drp_ie == NULL) { rsv->drp_ie = uwb_drp_ie_alloc(); @@ -148,9 +208,11 @@ int uwb_drp_ie_update(struct uwb_rsv *rsv) } drp_ie = rsv->drp_ie; + uwb_ie_drp_set_unsafe(drp_ie, unsafe); + uwb_ie_drp_set_tiebreaker(drp_ie, rsv->tiebreaker); uwb_ie_drp_set_owner(drp_ie, uwb_rsv_is_owner(rsv)); - uwb_ie_drp_set_status(drp_ie, status); - uwb_ie_drp_set_reason_code(drp_ie, reason_code); + uwb_ie_drp_set_status(drp_ie, uwb_rsv_status(rsv)); + uwb_ie_drp_set_reason_code(drp_ie, uwb_rsv_reason_code(rsv)); uwb_ie_drp_set_stream_index(drp_ie, rsv->stream); uwb_ie_drp_set_type(drp_ie, rsv->type); @@ -168,6 +230,27 @@ int uwb_drp_ie_update(struct uwb_rsv *rsv) uwb_drp_ie_from_bm(drp_ie, &rsv->mas); + if (uwb_rsv_has_two_drp_ies(rsv)) { + mv = &rsv->mv; + if (mv->companion_drp_ie == NULL) { + mv->companion_drp_ie = uwb_drp_ie_alloc(); + if (mv->companion_drp_ie == NULL) + return -ENOMEM; + } + drp_ie = mv->companion_drp_ie; + + /* keep all the same configuration of the main drp_ie */ + memcpy(drp_ie, rsv->drp_ie, sizeof(struct uwb_ie_drp)); + + + /* FIXME: handle properly the unsafe bit */ + uwb_ie_drp_set_unsafe(drp_ie, 1); + uwb_ie_drp_set_status(drp_ie, uwb_rsv_companion_status(rsv)); + uwb_ie_drp_set_reason_code(drp_ie, uwb_rsv_companion_reason_code(rsv)); + + uwb_drp_ie_from_bm(drp_ie, &mv->companion_mas); + } + rsv->ie_valid = true; return 0; } @@ -218,6 +301,8 @@ void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie) u8 zone; u16 zone_mask; + bitmap_zero(bm->bm, UWB_NUM_MAS); + for (cnt = 0; cnt < numallocs; cnt++) { alloc = &drp_ie->allocs[cnt]; zone_bm = le16_to_cpu(alloc->zone_bm); @@ -229,3 +314,4 @@ void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie) } } } + diff --git a/drivers/uwb/drp.c b/drivers/uwb/drp.c index fe328146adb7..2b4f9406789d 100644 --- a/drivers/uwb/drp.c +++ b/drivers/uwb/drp.c @@ -23,6 +23,59 @@ #include #include "uwb-internal.h" + +/* DRP Conflict Actions ([ECMA-368 2nd Edition] 17.4.6) */ +enum uwb_drp_conflict_action { + /* Reservation is mantained, no action needed */ + UWB_DRP_CONFLICT_MANTAIN = 0, + + /* the device shall not transmit frames in conflicting MASs in + * the following superframe. If the device is the reservation + * target, it shall also set the Reason Code in its DRP IE to + * Conflict in its beacon in the following superframe. + */ + UWB_DRP_CONFLICT_ACT1, + + /* the device shall not set the Reservation Status bit to ONE + * and shall not transmit frames in conflicting MASs. If the + * device is the reservation target, it shall also set the + * Reason Code in its DRP IE to Conflict. + */ + UWB_DRP_CONFLICT_ACT2, + + /* the device shall not transmit frames in conflicting MASs in + * the following superframe. It shall remove the conflicting + * MASs from the reservation or set the Reservation Status to + * ZERO in its beacon in the following superframe. If the + * device is the reservation target, it shall also set the + * Reason Code in its DRP IE to Conflict. + */ + UWB_DRP_CONFLICT_ACT3, +}; + + +static void uwb_rc_set_drp_cmd_done(struct uwb_rc *rc, void *arg, + struct uwb_rceb *reply, ssize_t reply_size) +{ + struct uwb_rc_evt_set_drp_ie *r = (struct uwb_rc_evt_set_drp_ie *)reply; + + if (r != NULL) { + if (r->bResultCode != UWB_RC_RES_SUCCESS) + dev_err(&rc->uwb_dev.dev, "SET-DRP-IE failed: %s (%d)\n", + uwb_rc_strerror(r->bResultCode), r->bResultCode); + } else + dev_err(&rc->uwb_dev.dev, "SET-DRP-IE: timeout\n"); + + spin_lock(&rc->rsvs_lock); + if (rc->set_drp_ie_pending > 1) { + rc->set_drp_ie_pending = 0; + uwb_rsv_queue_update(rc); + } else { + rc->set_drp_ie_pending = 0; + } + spin_unlock(&rc->rsvs_lock); +} + /** * Construct and send the SET DRP IE * @@ -46,18 +99,23 @@ int uwb_rc_send_all_drp_ie(struct uwb_rc *rc) { int result; - struct device *dev = &rc->uwb_dev.dev; struct uwb_rc_cmd_set_drp_ie *cmd; - struct uwb_rc_evt_set_drp_ie reply; struct uwb_rsv *rsv; + struct uwb_rsv_move *mv; int num_bytes = 0; u8 *IEDataptr; result = -ENOMEM; /* First traverse all reservations to determine memory needed. */ list_for_each_entry(rsv, &rc->reservations, rc_node) { - if (rsv->drp_ie != NULL) + if (rsv->drp_ie != NULL) { num_bytes += rsv->drp_ie->hdr.length + 2; + if (uwb_rsv_has_two_drp_ies(rsv) && + (rsv->mv.companion_drp_ie != NULL)) { + mv = &rsv->mv; + num_bytes += mv->companion_drp_ie->hdr.length + 2; + } + } } num_bytes += sizeof(rc->drp_avail.ie); cmd = kzalloc(sizeof(*cmd) + num_bytes, GFP_KERNEL); @@ -68,109 +126,322 @@ int uwb_rc_send_all_drp_ie(struct uwb_rc *rc) cmd->wIELength = num_bytes; IEDataptr = (u8 *)&cmd->IEData[0]; + /* FIXME: DRV avail IE is not always needed */ + /* put DRP avail IE first */ + memcpy(IEDataptr, &rc->drp_avail.ie, sizeof(rc->drp_avail.ie)); + IEDataptr += sizeof(struct uwb_ie_drp_avail); + /* Next traverse all reservations to place IEs in allocated memory. */ list_for_each_entry(rsv, &rc->reservations, rc_node) { if (rsv->drp_ie != NULL) { memcpy(IEDataptr, rsv->drp_ie, rsv->drp_ie->hdr.length + 2); IEDataptr += rsv->drp_ie->hdr.length + 2; + + if (uwb_rsv_has_two_drp_ies(rsv) && + (rsv->mv.companion_drp_ie != NULL)) { + mv = &rsv->mv; + memcpy(IEDataptr, mv->companion_drp_ie, + mv->companion_drp_ie->hdr.length + 2); + IEDataptr += mv->companion_drp_ie->hdr.length + 2; + } } } - memcpy(IEDataptr, &rc->drp_avail.ie, sizeof(rc->drp_avail.ie)); - reply.rceb.bEventType = UWB_RC_CET_GENERAL; - reply.rceb.wEvent = UWB_RC_CMD_SET_DRP_IE; - result = uwb_rc_cmd(rc, "SET-DRP-IE", &cmd->rccb, - sizeof(*cmd) + num_bytes, &reply.rceb, - sizeof(reply)); - if (result < 0) - goto error_cmd; - result = le16_to_cpu(reply.wRemainingSpace); - if (reply.bResultCode != UWB_RC_RES_SUCCESS) { - dev_err(&rc->uwb_dev.dev, "SET-DRP-IE: command execution " - "failed: %s (%d). RemainingSpace in beacon " - "= %d\n", uwb_rc_strerror(reply.bResultCode), - reply.bResultCode, result); - result = -EIO; - } else { - dev_dbg(dev, "SET-DRP-IE sent. RemainingSpace in beacon " - "= %d.\n", result); - result = 0; - } -error_cmd: + result = uwb_rc_cmd_async(rc, "SET-DRP-IE", &cmd->rccb, sizeof(*cmd) + num_bytes, + UWB_RC_CET_GENERAL, UWB_RC_CMD_SET_DRP_IE, + uwb_rc_set_drp_cmd_done, NULL); + + rc->set_drp_ie_pending = 1; + kfree(cmd); error: return result; } -void uwb_drp_handle_timeout(struct uwb_rsv *rsv) +/* + * Evaluate the action to perform using conflict resolution rules + * + * Return a uwb_drp_conflict_action. + */ +static int evaluate_conflict_action(struct uwb_ie_drp *ext_drp_ie, int ext_beacon_slot, + struct uwb_rsv *rsv, int our_status) { - struct device *dev = &rsv->rc->uwb_dev.dev; + int our_tie_breaker = rsv->tiebreaker; + int our_type = rsv->type; + int our_beacon_slot = rsv->rc->uwb_dev.beacon_slot; + + int ext_tie_breaker = uwb_ie_drp_tiebreaker(ext_drp_ie); + int ext_status = uwb_ie_drp_status(ext_drp_ie); + int ext_type = uwb_ie_drp_type(ext_drp_ie); + + + /* [ECMA-368 2nd Edition] 17.4.6 */ + if (ext_type == UWB_DRP_TYPE_PCA && our_type == UWB_DRP_TYPE_PCA) { + return UWB_DRP_CONFLICT_MANTAIN; + } + + /* [ECMA-368 2nd Edition] 17.4.6-1 */ + if (our_type == UWB_DRP_TYPE_ALIEN_BP) { + return UWB_DRP_CONFLICT_MANTAIN; + } + + /* [ECMA-368 2nd Edition] 17.4.6-2 */ + if (ext_type == UWB_DRP_TYPE_ALIEN_BP) { + /* here we know our_type != UWB_DRP_TYPE_ALIEN_BP */ + return UWB_DRP_CONFLICT_ACT1; + } + + /* [ECMA-368 2nd Edition] 17.4.6-3 */ + if (our_status == 0 && ext_status == 1) { + return UWB_DRP_CONFLICT_ACT2; + } - dev_dbg(dev, "reservation timeout in state %s (%d)\n", - uwb_rsv_state_str(rsv->state), rsv->state); + /* [ECMA-368 2nd Edition] 17.4.6-4 */ + if (our_status == 1 && ext_status == 0) { + return UWB_DRP_CONFLICT_MANTAIN; + } - switch (rsv->state) { - case UWB_RSV_STATE_O_INITIATED: - if (rsv->is_multicast) { - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); - return; + /* [ECMA-368 2nd Edition] 17.4.6-5a */ + if (our_tie_breaker == ext_tie_breaker && + our_beacon_slot < ext_beacon_slot) { + return UWB_DRP_CONFLICT_MANTAIN; + } + + /* [ECMA-368 2nd Edition] 17.4.6-5b */ + if (our_tie_breaker != ext_tie_breaker && + our_beacon_slot > ext_beacon_slot) { + return UWB_DRP_CONFLICT_MANTAIN; + } + + if (our_status == 0) { + if (our_tie_breaker == ext_tie_breaker) { + /* [ECMA-368 2nd Edition] 17.4.6-6a */ + if (our_beacon_slot > ext_beacon_slot) { + return UWB_DRP_CONFLICT_ACT2; + } + } else { + /* [ECMA-368 2nd Edition] 17.4.6-6b */ + if (our_beacon_slot < ext_beacon_slot) { + return UWB_DRP_CONFLICT_ACT2; + } } - break; - case UWB_RSV_STATE_O_ESTABLISHED: - if (rsv->is_multicast) - return; - break; - default: - break; + } else { + if (our_tie_breaker == ext_tie_breaker) { + /* [ECMA-368 2nd Edition] 17.4.6-7a */ + if (our_beacon_slot > ext_beacon_slot) { + return UWB_DRP_CONFLICT_ACT3; + } + } else { + /* [ECMA-368 2nd Edition] 17.4.6-7b */ + if (our_beacon_slot < ext_beacon_slot) { + return UWB_DRP_CONFLICT_ACT3; + } + } + } + return UWB_DRP_CONFLICT_MANTAIN; +} + +static void handle_conflict_normal(struct uwb_ie_drp *drp_ie, + int ext_beacon_slot, + struct uwb_rsv *rsv, + struct uwb_mas_bm *conflicting_mas) +{ + struct uwb_rc *rc = rsv->rc; + struct uwb_rsv_move *mv = &rsv->mv; + struct uwb_drp_backoff_win *bow = &rc->bow; + int action; + + action = evaluate_conflict_action(drp_ie, ext_beacon_slot, rsv, uwb_rsv_status(rsv)); + + if (uwb_rsv_is_owner(rsv)) { + switch(action) { + case UWB_DRP_CONFLICT_ACT2: + /* try move */ + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_TO_BE_MOVED); + if (bow->can_reserve_extra_mases == false) + uwb_rsv_backoff_win_increment(rc); + + break; + case UWB_DRP_CONFLICT_ACT3: + uwb_rsv_backoff_win_increment(rc); + /* drop some mases with reason modified */ + /* put in the companion the mases to be dropped */ + bitmap_and(mv->companion_mas.bm, rsv->mas.bm, conflicting_mas->bm, UWB_NUM_MAS); + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MODIFIED); + default: + break; + } + } else { + switch(action) { + case UWB_DRP_CONFLICT_ACT2: + case UWB_DRP_CONFLICT_ACT3: + uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_CONFLICT); + default: + break; + } + + } + +} + +static void handle_conflict_expanding(struct uwb_ie_drp *drp_ie, int ext_beacon_slot, + struct uwb_rsv *rsv, bool companion_only, + struct uwb_mas_bm *conflicting_mas) +{ + struct uwb_rc *rc = rsv->rc; + struct uwb_drp_backoff_win *bow = &rc->bow; + struct uwb_rsv_move *mv = &rsv->mv; + int action; + + if (companion_only) { + /* status of companion is 0 at this point */ + action = evaluate_conflict_action(drp_ie, ext_beacon_slot, rsv, 0); + if (uwb_rsv_is_owner(rsv)) { + switch(action) { + case UWB_DRP_CONFLICT_ACT2: + case UWB_DRP_CONFLICT_ACT3: + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); + rsv->needs_release_companion_mas = false; + if (bow->can_reserve_extra_mases == false) + uwb_rsv_backoff_win_increment(rc); + uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); + } + } else { /* rsv is target */ + switch(action) { + case UWB_DRP_CONFLICT_ACT2: + case UWB_DRP_CONFLICT_ACT3: + uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_EXPANDING_CONFLICT); + /* send_drp_avail_ie = true; */ + } + } + } else { /* also base part of the reservation is conflicting */ + if (uwb_rsv_is_owner(rsv)) { + uwb_rsv_backoff_win_increment(rc); + /* remove companion part */ + uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); + + /* drop some mases with reason modified */ + + /* put in the companion the mases to be dropped */ + bitmap_andnot(mv->companion_mas.bm, rsv->mas.bm, conflicting_mas->bm, UWB_NUM_MAS); + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MODIFIED); + } else { /* it is a target rsv */ + uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_CONFLICT); + /* send_drp_avail_ie = true; */ + } + } +} + +static void uwb_drp_handle_conflict_rsv(struct uwb_rc *rc, struct uwb_rsv *rsv, + struct uwb_rc_evt_drp *drp_evt, + struct uwb_ie_drp *drp_ie, + struct uwb_mas_bm *conflicting_mas) +{ + struct uwb_rsv_move *mv; + + /* check if the conflicting reservation has two drp_ies */ + if (uwb_rsv_has_two_drp_ies(rsv)) { + mv = &rsv->mv; + if (bitmap_intersects(rsv->mas.bm, conflicting_mas->bm, UWB_NUM_MAS)) { + handle_conflict_expanding(drp_ie, drp_evt->beacon_slot_number, + rsv, false, conflicting_mas); + } else { + if (bitmap_intersects(mv->companion_mas.bm, conflicting_mas->bm, UWB_NUM_MAS)) { + handle_conflict_expanding(drp_ie, drp_evt->beacon_slot_number, + rsv, true, conflicting_mas); + } + } + } else if (bitmap_intersects(rsv->mas.bm, conflicting_mas->bm, UWB_NUM_MAS)) { + handle_conflict_normal(drp_ie, drp_evt->beacon_slot_number, rsv, conflicting_mas); } - uwb_rsv_remove(rsv); } +static void uwb_drp_handle_all_conflict_rsv(struct uwb_rc *rc, + struct uwb_rc_evt_drp *drp_evt, + struct uwb_ie_drp *drp_ie, + struct uwb_mas_bm *conflicting_mas) +{ + struct uwb_rsv *rsv; + + list_for_each_entry(rsv, &rc->reservations, rc_node) { + uwb_drp_handle_conflict_rsv(rc, rsv, drp_evt, drp_ie, conflicting_mas); + } +} + /* * Based on the DRP IE, transition a target reservation to a new * state. */ static void uwb_drp_process_target(struct uwb_rc *rc, struct uwb_rsv *rsv, - struct uwb_ie_drp *drp_ie) + struct uwb_ie_drp *drp_ie, struct uwb_rc_evt_drp *drp_evt) { struct device *dev = &rc->uwb_dev.dev; + struct uwb_rsv_move *mv = &rsv->mv; int status; enum uwb_drp_reason reason_code; - + struct uwb_mas_bm mas; + status = uwb_ie_drp_status(drp_ie); reason_code = uwb_ie_drp_reason_code(drp_ie); + uwb_drp_ie_to_bm(&mas, drp_ie); - if (status) { - switch (reason_code) { - case UWB_DRP_REASON_ACCEPTED: - uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED); - break; - case UWB_DRP_REASON_MODIFIED: - dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", - reason_code, status); + switch (reason_code) { + case UWB_DRP_REASON_ACCEPTED: + + if (rsv->state == UWB_RSV_STATE_T_CONFLICT) { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_CONFLICT); break; - default: - dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", - reason_code, status); } - } else { - switch (reason_code) { - case UWB_DRP_REASON_ACCEPTED: - /* New reservations are handled in uwb_rsv_find(). */ - break; - case UWB_DRP_REASON_DENIED: - uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); - break; - case UWB_DRP_REASON_CONFLICT: - case UWB_DRP_REASON_MODIFIED: - dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", - reason_code, status); + + if (rsv->state == UWB_RSV_STATE_T_EXPANDING_ACCEPTED) { + /* drp_ie is companion */ + if (!bitmap_equal(rsv->mas.bm, mas.bm, UWB_NUM_MAS)) + /* stroke companion */ + uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_EXPANDING_ACCEPTED); + } else { + if (!bitmap_equal(rsv->mas.bm, mas.bm, UWB_NUM_MAS)) { + if (uwb_drp_avail_reserve_pending(rc, &mas) == -EBUSY) { + /* FIXME: there is a conflict, find + * the conflicting reservations and + * take a sensible action. Consider + * that in drp_ie there is the + * "neighbour" */ + uwb_drp_handle_all_conflict_rsv(rc, drp_evt, drp_ie, &mas); + } else { + /* accept the extra reservation */ + bitmap_copy(mv->companion_mas.bm, mas.bm, UWB_NUM_MAS); + uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_EXPANDING_ACCEPTED); + } + } else { + if (status) { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED); + } + } + + } + break; + + case UWB_DRP_REASON_MODIFIED: + /* check to see if we have already modified the reservation */ + if (bitmap_equal(rsv->mas.bm, mas.bm, UWB_NUM_MAS)) { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED); break; - default: - dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", - reason_code, status); } + + /* find if the owner wants to expand or reduce */ + if (bitmap_subset(mas.bm, rsv->mas.bm, UWB_NUM_MAS)) { + /* owner is reducing */ + bitmap_andnot(mv->companion_mas.bm, rsv->mas.bm, mas.bm, UWB_NUM_MAS); + uwb_drp_avail_release(rsv->rc, &mv->companion_mas); + } + + bitmap_copy(rsv->mas.bm, mas.bm, UWB_NUM_MAS); + uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_RESIZED); + break; + default: + dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", + reason_code, status); } } @@ -179,23 +450,60 @@ static void uwb_drp_process_target(struct uwb_rc *rc, struct uwb_rsv *rsv, * state. */ static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv, - struct uwb_ie_drp *drp_ie) + struct uwb_dev *src, struct uwb_ie_drp *drp_ie, + struct uwb_rc_evt_drp *drp_evt) { struct device *dev = &rc->uwb_dev.dev; + struct uwb_rsv_move *mv = &rsv->mv; int status; enum uwb_drp_reason reason_code; + struct uwb_mas_bm mas; status = uwb_ie_drp_status(drp_ie); reason_code = uwb_ie_drp_reason_code(drp_ie); + uwb_drp_ie_to_bm(&mas, drp_ie); if (status) { switch (reason_code) { case UWB_DRP_REASON_ACCEPTED: - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); - break; - case UWB_DRP_REASON_MODIFIED: - dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", - reason_code, status); + switch (rsv->state) { + case UWB_RSV_STATE_O_PENDING: + case UWB_RSV_STATE_O_INITIATED: + case UWB_RSV_STATE_O_ESTABLISHED: + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); + break; + case UWB_RSV_STATE_O_MODIFIED: + if (bitmap_equal(mas.bm, rsv->mas.bm, UWB_NUM_MAS)) { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); + } else { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MODIFIED); + } + break; + + case UWB_RSV_STATE_O_MOVE_REDUCING: /* shouldn' t be a problem */ + if (bitmap_equal(mas.bm, rsv->mas.bm, UWB_NUM_MAS)) { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); + } else { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); + } + break; + case UWB_RSV_STATE_O_MOVE_EXPANDING: + if (bitmap_equal(mas.bm, mv->companion_mas.bm, UWB_NUM_MAS)) { + /* Companion reservation accepted */ + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); + } else { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_EXPANDING); + } + break; + case UWB_RSV_STATE_O_MOVE_COMBINING: + if (bitmap_equal(mas.bm, rsv->mas.bm, UWB_NUM_MAS)) + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); + else + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); + break; + default: + break; + } break; default: dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", @@ -210,9 +518,10 @@ static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv, uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); break; case UWB_DRP_REASON_CONFLICT: - case UWB_DRP_REASON_MODIFIED: - dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", - reason_code, status); + /* resolve the conflict */ + bitmap_complement(mas.bm, src->last_availability_bm, + UWB_NUM_MAS); + uwb_drp_handle_conflict_rsv(rc, rsv, drp_evt, drp_ie, &mas); break; default: dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", @@ -221,12 +530,110 @@ static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv, } } +static void uwb_cnflt_alien_stroke_timer(struct uwb_cnflt_alien *cnflt) +{ + unsigned timeout_us = UWB_MAX_LOST_BEACONS * UWB_SUPERFRAME_LENGTH_US; + mod_timer(&cnflt->timer, jiffies + usecs_to_jiffies(timeout_us)); +} + +static void uwb_cnflt_update_work(struct work_struct *work) +{ + struct uwb_cnflt_alien *cnflt = container_of(work, + struct uwb_cnflt_alien, + cnflt_update_work); + struct uwb_cnflt_alien *c; + struct uwb_rc *rc = cnflt->rc; + + unsigned long delay_us = UWB_MAS_LENGTH_US * UWB_MAS_PER_ZONE; + + mutex_lock(&rc->rsvs_mutex); + + list_del(&cnflt->rc_node); + + /* update rc global conflicting alien bitmap */ + bitmap_zero(rc->cnflt_alien_bitmap.bm, UWB_NUM_MAS); + + list_for_each_entry(c, &rc->cnflt_alien_list, rc_node) { + bitmap_or(rc->cnflt_alien_bitmap.bm, rc->cnflt_alien_bitmap.bm, c->mas.bm, UWB_NUM_MAS); + } + + queue_delayed_work(rc->rsv_workq, &rc->rsv_alien_bp_work, usecs_to_jiffies(delay_us)); + + kfree(cnflt); + mutex_unlock(&rc->rsvs_mutex); +} + +static void uwb_cnflt_timer(unsigned long arg) +{ + struct uwb_cnflt_alien *cnflt = (struct uwb_cnflt_alien *)arg; + + queue_work(cnflt->rc->rsv_workq, &cnflt->cnflt_update_work); +} + /* - * Process a received DRP IE, it's either for a reservation owned by - * the RC or targeted at it (or it's for a WUSB cluster reservation). + * We have received an DRP_IE of type Alien BP and we need to make + * sure we do not transmit in conflicting MASs. */ -static void uwb_drp_process(struct uwb_rc *rc, struct uwb_dev *src, - struct uwb_ie_drp *drp_ie) +static void uwb_drp_handle_alien_drp(struct uwb_rc *rc, struct uwb_ie_drp *drp_ie) +{ + struct device *dev = &rc->uwb_dev.dev; + struct uwb_mas_bm mas; + struct uwb_cnflt_alien *cnflt; + char buf[72]; + unsigned long delay_us = UWB_MAS_LENGTH_US * UWB_MAS_PER_ZONE; + + uwb_drp_ie_to_bm(&mas, drp_ie); + bitmap_scnprintf(buf, sizeof(buf), mas.bm, UWB_NUM_MAS); + + list_for_each_entry(cnflt, &rc->cnflt_alien_list, rc_node) { + if (bitmap_equal(cnflt->mas.bm, mas.bm, UWB_NUM_MAS)) { + /* Existing alien BP reservation conflicting + * bitmap, just reset the timer */ + uwb_cnflt_alien_stroke_timer(cnflt); + return; + } + } + + /* New alien BP reservation conflicting bitmap */ + + /* alloc and initialize new uwb_cnflt_alien */ + cnflt = kzalloc(sizeof(struct uwb_cnflt_alien), GFP_KERNEL); + if (!cnflt) + dev_err(dev, "failed to alloc uwb_cnflt_alien struct\n"); + INIT_LIST_HEAD(&cnflt->rc_node); + init_timer(&cnflt->timer); + cnflt->timer.function = uwb_cnflt_timer; + cnflt->timer.data = (unsigned long)cnflt; + + cnflt->rc = rc; + INIT_WORK(&cnflt->cnflt_update_work, uwb_cnflt_update_work); + + bitmap_copy(cnflt->mas.bm, mas.bm, UWB_NUM_MAS); + + list_add_tail(&cnflt->rc_node, &rc->cnflt_alien_list); + + /* update rc global conflicting alien bitmap */ + bitmap_or(rc->cnflt_alien_bitmap.bm, rc->cnflt_alien_bitmap.bm, mas.bm, UWB_NUM_MAS); + + queue_delayed_work(rc->rsv_workq, &rc->rsv_alien_bp_work, usecs_to_jiffies(delay_us)); + + /* start the timer */ + uwb_cnflt_alien_stroke_timer(cnflt); +} + +static void uwb_drp_process_not_involved(struct uwb_rc *rc, + struct uwb_rc_evt_drp *drp_evt, + struct uwb_ie_drp *drp_ie) +{ + struct uwb_mas_bm mas; + + uwb_drp_ie_to_bm(&mas, drp_ie); + uwb_drp_handle_all_conflict_rsv(rc, drp_evt, drp_ie, &mas); +} + +static void uwb_drp_process_involved(struct uwb_rc *rc, struct uwb_dev *src, + struct uwb_rc_evt_drp *drp_evt, + struct uwb_ie_drp *drp_ie) { struct uwb_rsv *rsv; @@ -239,7 +646,7 @@ static void uwb_drp_process(struct uwb_rc *rc, struct uwb_dev *src, */ return; } - + /* * Do nothing with DRP IEs for reservations that have been * terminated. @@ -248,13 +655,43 @@ static void uwb_drp_process(struct uwb_rc *rc, struct uwb_dev *src, uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); return; } - + if (uwb_ie_drp_owner(drp_ie)) - uwb_drp_process_target(rc, rsv, drp_ie); + uwb_drp_process_target(rc, rsv, drp_ie, drp_evt); + else + uwb_drp_process_owner(rc, rsv, src, drp_ie, drp_evt); + +} + + +static bool uwb_drp_involves_us(struct uwb_rc *rc, struct uwb_ie_drp *drp_ie) +{ + return uwb_dev_addr_cmp(&rc->uwb_dev.dev_addr, &drp_ie->dev_addr) == 0; +} + +/* + * Process a received DRP IE. + */ +static void uwb_drp_process(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt, + struct uwb_dev *src, struct uwb_ie_drp *drp_ie) +{ + if (uwb_ie_drp_type(drp_ie) == UWB_DRP_TYPE_ALIEN_BP) + uwb_drp_handle_alien_drp(rc, drp_ie); + else if (uwb_drp_involves_us(rc, drp_ie)) + uwb_drp_process_involved(rc, src, drp_evt, drp_ie); else - uwb_drp_process_owner(rc, rsv, drp_ie); + uwb_drp_process_not_involved(rc, drp_evt, drp_ie); } +/* + * Process a received DRP Availability IE + */ +static void uwb_drp_availability_process(struct uwb_rc *rc, struct uwb_dev *src, + struct uwb_ie_drp_avail *drp_availability_ie) +{ + bitmap_copy(src->last_availability_bm, + drp_availability_ie->bmp, UWB_NUM_MAS); +} /* * Process all the DRP IEs (both DRP IEs and the DRP Availability IE) @@ -276,10 +713,10 @@ void uwb_drp_process_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt, switch (ie_hdr->element_id) { case UWB_IE_DRP_AVAILABILITY: - /* FIXME: does something need to be done with this? */ + uwb_drp_availability_process(rc, src_dev, (struct uwb_ie_drp_avail *)ie_hdr); break; case UWB_IE_DRP: - uwb_drp_process(rc, src_dev, (struct uwb_ie_drp *)ie_hdr); + uwb_drp_process(rc, drp_evt, src_dev, (struct uwb_ie_drp *)ie_hdr); break; default: dev_warn(dev, "unexpected IE in DRP notification\n"); @@ -292,55 +729,6 @@ void uwb_drp_process_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt, (int)ielen); } - -/* - * Go through all the DRP IEs and find the ones that conflict with our - * reservations. - * - * FIXME: must resolve the conflict according the the rules in - * [ECMA-368]. - */ -static -void uwb_drp_process_conflict_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt, - size_t ielen, struct uwb_dev *src_dev) -{ - struct device *dev = &rc->uwb_dev.dev; - struct uwb_ie_hdr *ie_hdr; - struct uwb_ie_drp *drp_ie; - void *ptr; - - ptr = drp_evt->ie_data; - for (;;) { - ie_hdr = uwb_ie_next(&ptr, &ielen); - if (!ie_hdr) - break; - - drp_ie = container_of(ie_hdr, struct uwb_ie_drp, hdr); - - /* FIXME: check if this DRP IE conflicts. */ - } - - if (ielen > 0) - dev_warn(dev, "%d octets remaining in DRP notification\n", - (int)ielen); -} - - -/* - * Terminate all reservations owned by, or targeted at, 'uwb_dev'. - */ -static void uwb_drp_terminate_all(struct uwb_rc *rc, struct uwb_dev *uwb_dev) -{ - struct uwb_rsv *rsv; - - list_for_each_entry(rsv, &rc->reservations, rc_node) { - if (rsv->owner == uwb_dev - || (rsv->target.type == UWB_RSV_TARGET_DEV && rsv->target.dev == uwb_dev)) - uwb_rsv_remove(rsv); - } -} - - /** * uwbd_evt_handle_rc_drp - handle a DRP_IE event * @evt: the DRP_IE event from the radio controller @@ -381,7 +769,6 @@ int uwbd_evt_handle_rc_drp(struct uwb_event *evt) size_t ielength, bytes_left; struct uwb_dev_addr src_addr; struct uwb_dev *src_dev; - int reason; /* Is there enough data to decode the event (and any IEs in its payload)? */ @@ -417,22 +804,8 @@ int uwbd_evt_handle_rc_drp(struct uwb_event *evt) mutex_lock(&rc->rsvs_mutex); - reason = uwb_rc_evt_drp_reason(drp_evt); - - switch (reason) { - case UWB_DRP_NOTIF_DRP_IE_RCVD: - uwb_drp_process_all(rc, drp_evt, ielength, src_dev); - break; - case UWB_DRP_NOTIF_CONFLICT: - uwb_drp_process_conflict_all(rc, drp_evt, ielength, src_dev); - break; - case UWB_DRP_NOTIF_TERMINATE: - uwb_drp_terminate_all(rc, src_dev); - break; - default: - dev_warn(dev, "ignored DRP event with reason code: %d\n", reason); - break; - } + /* We do not distinguish from the reason */ + uwb_drp_process_all(rc, drp_evt, ielength, src_dev); mutex_unlock(&rc->rsvs_mutex); diff --git a/drivers/uwb/rsv.c b/drivers/uwb/rsv.c index 1cd84f927540..165aec6a8f97 100644 --- a/drivers/uwb/rsv.c +++ b/drivers/uwb/rsv.c @@ -17,20 +17,31 @@ */ #include #include +#include #include "uwb-internal.h" static void uwb_rsv_timer(unsigned long arg); static const char *rsv_states[] = { - [UWB_RSV_STATE_NONE] = "none", - [UWB_RSV_STATE_O_INITIATED] = "initiated", - [UWB_RSV_STATE_O_PENDING] = "pending", - [UWB_RSV_STATE_O_MODIFIED] = "modified", - [UWB_RSV_STATE_O_ESTABLISHED] = "established", - [UWB_RSV_STATE_T_ACCEPTED] = "accepted", - [UWB_RSV_STATE_T_DENIED] = "denied", - [UWB_RSV_STATE_T_PENDING] = "pending", + [UWB_RSV_STATE_NONE] = "none ", + [UWB_RSV_STATE_O_INITIATED] = "o initiated ", + [UWB_RSV_STATE_O_PENDING] = "o pending ", + [UWB_RSV_STATE_O_MODIFIED] = "o modified ", + [UWB_RSV_STATE_O_ESTABLISHED] = "o established ", + [UWB_RSV_STATE_O_TO_BE_MOVED] = "o to be moved ", + [UWB_RSV_STATE_O_MOVE_EXPANDING] = "o move expanding", + [UWB_RSV_STATE_O_MOVE_COMBINING] = "o move combining", + [UWB_RSV_STATE_O_MOVE_REDUCING] = "o move reducing ", + [UWB_RSV_STATE_T_ACCEPTED] = "t accepted ", + [UWB_RSV_STATE_T_CONFLICT] = "t conflict ", + [UWB_RSV_STATE_T_PENDING] = "t pending ", + [UWB_RSV_STATE_T_DENIED] = "t denied ", + [UWB_RSV_STATE_T_RESIZED] = "t resized ", + [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = "t expanding acc ", + [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = "t expanding conf", + [UWB_RSV_STATE_T_EXPANDING_PENDING] = "t expanding pend", + [UWB_RSV_STATE_T_EXPANDING_DENIED] = "t expanding den ", }; static const char *rsv_types[] = { @@ -41,6 +52,31 @@ static const char *rsv_types[] = { [UWB_DRP_TYPE_PCA] = "pca", }; +bool uwb_rsv_has_two_drp_ies(struct uwb_rsv *rsv) +{ + static const bool has_two_drp_ies[] = { + [UWB_RSV_STATE_O_INITIATED] = false, + [UWB_RSV_STATE_O_PENDING] = false, + [UWB_RSV_STATE_O_MODIFIED] = false, + [UWB_RSV_STATE_O_ESTABLISHED] = false, + [UWB_RSV_STATE_O_TO_BE_MOVED] = false, + [UWB_RSV_STATE_O_MOVE_COMBINING] = false, + [UWB_RSV_STATE_O_MOVE_REDUCING] = false, + [UWB_RSV_STATE_O_MOVE_EXPANDING] = true, + [UWB_RSV_STATE_T_ACCEPTED] = false, + [UWB_RSV_STATE_T_CONFLICT] = false, + [UWB_RSV_STATE_T_PENDING] = false, + [UWB_RSV_STATE_T_DENIED] = false, + [UWB_RSV_STATE_T_RESIZED] = false, + [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = true, + [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = true, + [UWB_RSV_STATE_T_EXPANDING_PENDING] = true, + [UWB_RSV_STATE_T_EXPANDING_DENIED] = true, + }; + + return has_two_drp_ies[rsv->state]; +} + /** * uwb_rsv_state_str - return a string for a reservation state * @state: the reservation state. @@ -65,7 +101,7 @@ const char *uwb_rsv_type_str(enum uwb_drp_type type) } EXPORT_SYMBOL_GPL(uwb_rsv_type_str); -static void uwb_rsv_dump(struct uwb_rsv *rsv) +void uwb_rsv_dump(char *text, struct uwb_rsv *rsv) { struct device *dev = &rsv->rc->uwb_dev.dev; struct uwb_dev_addr devaddr; @@ -88,12 +124,12 @@ static void uwb_rsv_release(struct kref *kref) kfree(rsv); } -static void uwb_rsv_get(struct uwb_rsv *rsv) +void uwb_rsv_get(struct uwb_rsv *rsv) { kref_get(&rsv->kref); } -static void uwb_rsv_put(struct uwb_rsv *rsv) +void uwb_rsv_put(struct uwb_rsv *rsv) { kref_put(&rsv->kref, uwb_rsv_release); } @@ -108,6 +144,7 @@ static void uwb_rsv_put(struct uwb_rsv *rsv) static int uwb_rsv_get_stream(struct uwb_rsv *rsv) { struct uwb_rc *rc = rsv->rc; + struct device *dev = &rc->uwb_dev.dev; unsigned long *streams_bm; int stream; @@ -129,12 +166,15 @@ static int uwb_rsv_get_stream(struct uwb_rsv *rsv) rsv->stream = stream; set_bit(stream, streams_bm); + dev_dbg(dev, "get stream %d\n", rsv->stream); + return 0; } static void uwb_rsv_put_stream(struct uwb_rsv *rsv) { struct uwb_rc *rc = rsv->rc; + struct device *dev = &rc->uwb_dev.dev; unsigned long *streams_bm; switch (rsv->target.type) { @@ -149,86 +189,52 @@ static void uwb_rsv_put_stream(struct uwb_rsv *rsv) } clear_bit(rsv->stream, streams_bm); + + dev_dbg(dev, "put stream %d\n", rsv->stream); } -/* - * Generate a MAS allocation with a single row component. - */ -static void uwb_rsv_gen_alloc_row(struct uwb_mas_bm *mas, - int first_mas, int mas_per_zone, - int zs, int ze) +void uwb_rsv_backoff_win_timer(unsigned long arg) { - struct uwb_mas_bm col; - int z; + struct uwb_drp_backoff_win *bow = (struct uwb_drp_backoff_win *)arg; + struct uwb_rc *rc = container_of(bow, struct uwb_rc, bow); + struct device *dev = &rc->uwb_dev.dev; - bitmap_zero(mas->bm, UWB_NUM_MAS); - bitmap_zero(col.bm, UWB_NUM_MAS); - bitmap_fill(col.bm, mas_per_zone); - bitmap_shift_left(col.bm, col.bm, first_mas + zs * UWB_MAS_PER_ZONE, UWB_NUM_MAS); - - for (z = zs; z <= ze; z++) { - bitmap_or(mas->bm, mas->bm, col.bm, UWB_NUM_MAS); - bitmap_shift_left(col.bm, col.bm, UWB_MAS_PER_ZONE, UWB_NUM_MAS); + bow->can_reserve_extra_mases = true; + if (bow->total_expired <= 4) { + bow->total_expired++; + } else { + /* after 4 backoff window has expired we can exit from + * the backoff procedure */ + bow->total_expired = 0; + bow->window = UWB_DRP_BACKOFF_WIN_MIN >> 1; } + dev_dbg(dev, "backoff_win_timer total_expired=%d, n=%d\n: ", bow->total_expired, bow->n); + + /* try to relocate all the "to be moved" relocations */ + uwb_rsv_handle_drp_avail_change(rc); } -/* - * Allocate some MAS for this reservation based on current local - * availability, the reservation parameters (max_mas, min_mas, - * sparsity), and the WiMedia rules for MAS allocations. - * - * Returns -EBUSY is insufficient free MAS are available. - * - * FIXME: to simplify this, only safe reservations with a single row - * component in zones 1 to 15 are tried (zone 0 is skipped to avoid - * problems with the MAS reserved for the BP). - * - * [ECMA-368] section B.2. - */ -static int uwb_rsv_alloc_mas(struct uwb_rsv *rsv) +void uwb_rsv_backoff_win_increment(struct uwb_rc *rc) { - static const int safe_mas_in_row[UWB_NUM_ZONES] = { - 8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, - }; - int n, r; - struct uwb_mas_bm mas; - bool found = false; + struct uwb_drp_backoff_win *bow = &rc->bow; + struct device *dev = &rc->uwb_dev.dev; + unsigned timeout_us; - /* - * Search all valid safe allocations until either: too few MAS - * are available; or the smallest allocation with sufficient - * MAS is found. - * - * The top of the zones are preferred, so space for larger - * allocations is available in the bottom of the zone (e.g., a - * 15 MAS allocation should start in row 14 leaving space for - * a 120 MAS allocation at row 0). - */ - for (n = safe_mas_in_row[0]; n >= 1; n--) { - int num_mas; + dev_dbg(dev, "backoff_win_increment: window=%d\n", bow->window); - num_mas = n * (UWB_NUM_ZONES - 1); - if (num_mas < rsv->min_mas) - break; - if (found && num_mas < rsv->max_mas) - break; + bow->can_reserve_extra_mases = false; - for (r = UWB_MAS_PER_ZONE-1; r >= 0; r--) { - if (safe_mas_in_row[r] < n) - continue; - uwb_rsv_gen_alloc_row(&mas, r, n, 1, UWB_NUM_ZONES); - if (uwb_drp_avail_reserve_pending(rsv->rc, &mas) == 0) { - found = true; - break; - } - } - } + if((bow->window << 1) == UWB_DRP_BACKOFF_WIN_MAX) + return; - if (!found) - return -EBUSY; + bow->window <<= 1; + bow->n = random32() & (bow->window - 1); + dev_dbg(dev, "new_window=%d, n=%d\n: ", bow->window, bow->n); - bitmap_copy(rsv->mas.bm, mas.bm, UWB_NUM_MAS); - return 0; + /* reset the timer associated variables */ + timeout_us = bow->n * UWB_SUPERFRAME_LENGTH_US; + bow->total_expired = 0; + mod_timer(&bow->timer, jiffies + usecs_to_jiffies(timeout_us)); } static void uwb_rsv_stroke_timer(struct uwb_rsv *rsv) @@ -241,13 +247,16 @@ static void uwb_rsv_stroke_timer(struct uwb_rsv *rsv) * received. */ if (rsv->is_multicast) { - if (rsv->state == UWB_RSV_STATE_O_INITIATED) + if (rsv->state == UWB_RSV_STATE_O_INITIATED + || rsv->state == UWB_RSV_STATE_O_MOVE_EXPANDING + || rsv->state == UWB_RSV_STATE_O_MOVE_COMBINING + || rsv->state == UWB_RSV_STATE_O_MOVE_REDUCING) sframes = 1; if (rsv->state == UWB_RSV_STATE_O_ESTABLISHED) sframes = 0; + } - rsv->expired = false; if (sframes > 0) { /* * Add an additional 2 superframes to account for the @@ -269,7 +278,7 @@ static void uwb_rsv_state_update(struct uwb_rsv *rsv, rsv->state = new_state; rsv->ie_valid = false; - uwb_rsv_dump(rsv); + uwb_rsv_dump("SU", rsv); uwb_rsv_stroke_timer(rsv); uwb_rsv_sched_update(rsv->rc); @@ -283,10 +292,17 @@ static void uwb_rsv_callback(struct uwb_rsv *rsv) void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state) { + struct uwb_rsv_move *mv = &rsv->mv; + if (rsv->state == new_state) { switch (rsv->state) { case UWB_RSV_STATE_O_ESTABLISHED: + case UWB_RSV_STATE_O_MOVE_EXPANDING: + case UWB_RSV_STATE_O_MOVE_COMBINING: + case UWB_RSV_STATE_O_MOVE_REDUCING: case UWB_RSV_STATE_T_ACCEPTED: + case UWB_RSV_STATE_T_EXPANDING_ACCEPTED: + case UWB_RSV_STATE_T_RESIZED: case UWB_RSV_STATE_NONE: uwb_rsv_stroke_timer(rsv); break; @@ -298,11 +314,10 @@ void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state) return; } + uwb_rsv_dump("SC", rsv); + switch (new_state) { case UWB_RSV_STATE_NONE: - uwb_drp_avail_release(rsv->rc, &rsv->mas); - if (uwb_rsv_is_owner(rsv)) - uwb_rsv_put_stream(rsv); uwb_rsv_state_update(rsv, UWB_RSV_STATE_NONE); uwb_rsv_callback(rsv); break; @@ -312,12 +327,45 @@ void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state) case UWB_RSV_STATE_O_PENDING: uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_PENDING); break; + case UWB_RSV_STATE_O_MODIFIED: + /* in the companion there are the MASes to drop */ + bitmap_andnot(rsv->mas.bm, rsv->mas.bm, mv->companion_mas.bm, UWB_NUM_MAS); + uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MODIFIED); + break; case UWB_RSV_STATE_O_ESTABLISHED: + if (rsv->state == UWB_RSV_STATE_O_MODIFIED + || rsv->state == UWB_RSV_STATE_O_MOVE_REDUCING) { + uwb_drp_avail_release(rsv->rc, &mv->companion_mas); + rsv->needs_release_companion_mas = false; + } uwb_drp_avail_reserve(rsv->rc, &rsv->mas); uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_ESTABLISHED); uwb_rsv_callback(rsv); break; + case UWB_RSV_STATE_O_MOVE_EXPANDING: + rsv->needs_release_companion_mas = true; + uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MOVE_EXPANDING); + break; + case UWB_RSV_STATE_O_MOVE_COMBINING: + rsv->needs_release_companion_mas = false; + uwb_drp_avail_reserve(rsv->rc, &mv->companion_mas); + bitmap_or(rsv->mas.bm, rsv->mas.bm, mv->companion_mas.bm, UWB_NUM_MAS); + rsv->mas.safe += mv->companion_mas.safe; + rsv->mas.unsafe += mv->companion_mas.unsafe; + uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); + break; + case UWB_RSV_STATE_O_MOVE_REDUCING: + bitmap_andnot(mv->companion_mas.bm, rsv->mas.bm, mv->final_mas.bm, UWB_NUM_MAS); + rsv->needs_release_companion_mas = true; + rsv->mas.safe = mv->final_mas.safe; + rsv->mas.unsafe = mv->final_mas.unsafe; + bitmap_copy(rsv->mas.bm, mv->final_mas.bm, UWB_NUM_MAS); + bitmap_copy(rsv->mas.unsafe_bm, mv->final_mas.unsafe_bm, UWB_NUM_MAS); + uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); + break; case UWB_RSV_STATE_T_ACCEPTED: + case UWB_RSV_STATE_T_RESIZED: + rsv->needs_release_companion_mas = false; uwb_drp_avail_reserve(rsv->rc, &rsv->mas); uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_ACCEPTED); uwb_rsv_callback(rsv); @@ -325,12 +373,82 @@ void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state) case UWB_RSV_STATE_T_DENIED: uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_DENIED); break; + case UWB_RSV_STATE_T_CONFLICT: + uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_CONFLICT); + break; + case UWB_RSV_STATE_T_PENDING: + uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_PENDING); + break; + case UWB_RSV_STATE_T_EXPANDING_ACCEPTED: + rsv->needs_release_companion_mas = true; + uwb_drp_avail_reserve(rsv->rc, &mv->companion_mas); + uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_EXPANDING_ACCEPTED); + break; default: dev_err(&rsv->rc->uwb_dev.dev, "unhandled state: %s (%d)\n", uwb_rsv_state_str(new_state), new_state); } } +static void uwb_rsv_handle_timeout_work(struct work_struct *work) +{ + struct uwb_rsv *rsv = container_of(work, struct uwb_rsv, + handle_timeout_work); + struct uwb_rc *rc = rsv->rc; + + mutex_lock(&rc->rsvs_mutex); + + uwb_rsv_dump("TO", rsv); + + switch (rsv->state) { + case UWB_RSV_STATE_O_INITIATED: + if (rsv->is_multicast) { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); + goto unlock; + } + break; + case UWB_RSV_STATE_O_MOVE_EXPANDING: + if (rsv->is_multicast) { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); + goto unlock; + } + break; + case UWB_RSV_STATE_O_MOVE_COMBINING: + if (rsv->is_multicast) { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); + goto unlock; + } + break; + case UWB_RSV_STATE_O_MOVE_REDUCING: + if (rsv->is_multicast) { + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); + goto unlock; + } + break; + case UWB_RSV_STATE_O_ESTABLISHED: + if (rsv->is_multicast) + goto unlock; + break; + case UWB_RSV_STATE_T_EXPANDING_ACCEPTED: + /* + * The time out could be for the main or of the + * companion DRP, assume it's for the companion and + * drop that first. A further time out is required to + * drop the main. + */ + uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED); + uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); + goto unlock; + default: + break; + } + + uwb_rsv_remove(rsv); + +unlock: + mutex_unlock(&rc->rsvs_mutex); +} + static struct uwb_rsv *uwb_rsv_alloc(struct uwb_rc *rc) { struct uwb_rsv *rsv; @@ -347,6 +465,7 @@ static struct uwb_rsv *uwb_rsv_alloc(struct uwb_rc *rc) rsv->timer.data = (unsigned long)rsv; rsv->rc = rc; + INIT_WORK(&rsv->handle_timeout_work, uwb_rsv_handle_timeout_work); return rsv; } @@ -381,8 +500,18 @@ EXPORT_SYMBOL_GPL(uwb_rsv_create); void uwb_rsv_remove(struct uwb_rsv *rsv) { + uwb_rsv_dump("RM", rsv); + if (rsv->state != UWB_RSV_STATE_NONE) uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); + + if (rsv->needs_release_companion_mas) + uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); + uwb_drp_avail_release(rsv->rc, &rsv->mas); + + if (uwb_rsv_is_owner(rsv)) + uwb_rsv_put_stream(rsv); + del_timer_sync(&rsv->timer); uwb_dev_put(rsv->owner); if (rsv->target.type == UWB_RSV_TARGET_DEV) @@ -409,7 +538,7 @@ EXPORT_SYMBOL_GPL(uwb_rsv_destroy); * @rsv: the reservation * * The PAL should fill in @rsv's owner, target, type, max_mas, - * min_mas, sparsity and is_multicast fields. If the target is a + * min_mas, max_interval and is_multicast fields. If the target is a * uwb_dev it must be referenced. * * The reservation's callback will be called when the reservation is @@ -418,16 +547,27 @@ EXPORT_SYMBOL_GPL(uwb_rsv_destroy); int uwb_rsv_establish(struct uwb_rsv *rsv) { struct uwb_rc *rc = rsv->rc; + struct uwb_mas_bm available; int ret; mutex_lock(&rc->rsvs_mutex); - ret = uwb_rsv_get_stream(rsv); if (ret) goto out; - ret = uwb_rsv_alloc_mas(rsv); - if (ret) { + rsv->tiebreaker = random32() & 1; + /* get available mas bitmap */ + uwb_drp_available(rc, &available); + + ret = uwb_rsv_find_best_allocation(rsv, &available, &rsv->mas); + if (ret == UWB_RSV_ALLOC_NOT_FOUND) { + ret = -EBUSY; + uwb_rsv_put_stream(rsv); + goto out; + } + + ret = uwb_drp_avail_reserve_pending(rc, &rsv->mas); + if (ret != 0) { uwb_rsv_put_stream(rsv); goto out; } @@ -448,16 +588,71 @@ EXPORT_SYMBOL_GPL(uwb_rsv_establish); * @rsv: the reservation to modify * @max_mas: new maximum MAS to reserve * @min_mas: new minimum MAS to reserve - * @sparsity: new sparsity to use + * @max_interval: new max_interval to use * * FIXME: implement this once there are PALs that use it. */ -int uwb_rsv_modify(struct uwb_rsv *rsv, int max_mas, int min_mas, int sparsity) +int uwb_rsv_modify(struct uwb_rsv *rsv, int max_mas, int min_mas, int max_interval) { return -ENOSYS; } EXPORT_SYMBOL_GPL(uwb_rsv_modify); +/* + * move an already established reservation (rc->rsvs_mutex must to be + * taken when tis function is called) + */ +int uwb_rsv_try_move(struct uwb_rsv *rsv, struct uwb_mas_bm *available) +{ + struct uwb_rc *rc = rsv->rc; + struct uwb_drp_backoff_win *bow = &rc->bow; + struct device *dev = &rc->uwb_dev.dev; + struct uwb_rsv_move *mv; + int ret = 0; + + if (bow->can_reserve_extra_mases == false) + return -EBUSY; + + mv = &rsv->mv; + + if (uwb_rsv_find_best_allocation(rsv, available, &mv->final_mas) == UWB_RSV_ALLOC_FOUND) { + + if (!bitmap_equal(rsv->mas.bm, mv->final_mas.bm, UWB_NUM_MAS)) { + /* We want to move the reservation */ + bitmap_andnot(mv->companion_mas.bm, mv->final_mas.bm, rsv->mas.bm, UWB_NUM_MAS); + uwb_drp_avail_reserve_pending(rc, &mv->companion_mas); + uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_EXPANDING); + } + } else { + dev_dbg(dev, "new allocation not found\n"); + } + + return ret; +} + +/* It will try to move every reservation in state O_ESTABLISHED giving + * to the MAS allocator algorithm an availability that is the real one + * plus the allocation already established from the reservation. */ +void uwb_rsv_handle_drp_avail_change(struct uwb_rc *rc) +{ + struct uwb_drp_backoff_win *bow = &rc->bow; + struct uwb_rsv *rsv; + struct uwb_mas_bm mas; + + if (bow->can_reserve_extra_mases == false) + return; + + list_for_each_entry(rsv, &rc->reservations, rc_node) { + if (rsv->state == UWB_RSV_STATE_O_ESTABLISHED || + rsv->state == UWB_RSV_STATE_O_TO_BE_MOVED) { + uwb_drp_available(rc, &mas); + bitmap_or(mas.bm, mas.bm, rsv->mas.bm, UWB_NUM_MAS); + uwb_rsv_try_move(rsv, &mas); + } + } + +} + /** * uwb_rsv_terminate - terminate an established reservation * @rsv: the reservation to terminate @@ -546,6 +741,7 @@ static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc, uwb_dev_get(rsv->owner); rsv->target.type = UWB_RSV_TARGET_DEV; rsv->target.dev = &rc->uwb_dev; + uwb_dev_get(&rc->uwb_dev); rsv->type = uwb_ie_drp_type(drp_ie); rsv->stream = uwb_ie_drp_stream_index(drp_ie); uwb_drp_ie_to_bm(&rsv->mas, drp_ie); @@ -567,11 +763,33 @@ static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc, list_add_tail(&rsv->rc_node, &rc->reservations); state = rsv->state; rsv->state = UWB_RSV_STATE_NONE; - uwb_rsv_set_state(rsv, state); + + /* FIXME: do something sensible here */ + if (state == UWB_RSV_STATE_T_ACCEPTED + && uwb_drp_avail_reserve_pending(rc, &rsv->mas) == -EBUSY) { + /* FIXME: do something sensible here */ + } else { + uwb_rsv_set_state(rsv, state); + } return rsv; } +/** + * uwb_rsv_get_usable_mas - get the bitmap of the usable MAS of a reservations + * @rsv: the reservation. + * @mas: returns the available MAS. + * + * The usable MAS of a reservation may be less than the negotiated MAS + * if alien BPs are present. + */ +void uwb_rsv_get_usable_mas(struct uwb_rsv *rsv, struct uwb_mas_bm *mas) +{ + bitmap_zero(mas->bm, UWB_NUM_MAS); + bitmap_andnot(mas->bm, rsv->mas.bm, rsv->rc->cnflt_alien_bitmap.bm, UWB_NUM_MAS); +} +EXPORT_SYMBOL_GPL(uwb_rsv_get_usable_mas); + /** * uwb_rsv_find - find a reservation for a received DRP IE. * @rc: the radio controller @@ -611,8 +829,6 @@ static bool uwb_rsv_update_all(struct uwb_rc *rc) bool ie_updated = false; list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) { - if (rsv->expired) - uwb_drp_handle_timeout(rsv); if (!rsv->ie_valid) { uwb_drp_ie_update(rsv); ie_updated = true; @@ -622,9 +838,47 @@ static bool uwb_rsv_update_all(struct uwb_rc *rc) return ie_updated; } +void uwb_rsv_queue_update(struct uwb_rc *rc) +{ + unsigned long delay_us = UWB_MAS_LENGTH_US * UWB_MAS_PER_ZONE; + + queue_delayed_work(rc->rsv_workq, &rc->rsv_update_work, usecs_to_jiffies(delay_us)); +} + +/** + * uwb_rsv_sched_update - schedule an update of the DRP IEs + * @rc: the radio controller. + * + * To improve performance and ensure correctness with [ECMA-368] the + * number of SET-DRP-IE commands that are done are limited. + * + * DRP IEs update come from two sources: DRP events from the hardware + * which all occur at the beginning of the superframe ('syncronous' + * events) and reservation establishment/termination requests from + * PALs or timers ('asynchronous' events). + * + * A delayed work ensures that all the synchronous events result in + * one SET-DRP-IE command. + * + * Additional logic (the set_drp_ie_pending and rsv_updated_postponed + * flags) will prevent an asynchrous event starting a SET-DRP-IE + * command if one is currently awaiting a response. + * + * FIXME: this does leave a window where an asynchrous event can delay + * the SET-DRP-IE for a synchronous event by one superframe. + */ void uwb_rsv_sched_update(struct uwb_rc *rc) { - queue_work(rc->rsv_workq, &rc->rsv_update_work); + spin_lock(&rc->rsvs_lock); + if (!delayed_work_pending(&rc->rsv_update_work)) { + if (rc->set_drp_ie_pending > 0) { + rc->set_drp_ie_pending++; + goto unlock; + } + uwb_rsv_queue_update(rc); + } +unlock: + spin_unlock(&rc->rsvs_lock); } /* @@ -633,7 +887,8 @@ void uwb_rsv_sched_update(struct uwb_rc *rc) */ static void uwb_rsv_update_work(struct work_struct *work) { - struct uwb_rc *rc = container_of(work, struct uwb_rc, rsv_update_work); + struct uwb_rc *rc = container_of(work, struct uwb_rc, + rsv_update_work.work); bool ie_updated; mutex_lock(&rc->rsvs_mutex); @@ -645,18 +900,34 @@ static void uwb_rsv_update_work(struct work_struct *work) ie_updated = true; } - if (ie_updated) + if (ie_updated && (rc->set_drp_ie_pending == 0)) uwb_rc_send_all_drp_ie(rc); mutex_unlock(&rc->rsvs_mutex); } +static void uwb_rsv_alien_bp_work(struct work_struct *work) +{ + struct uwb_rc *rc = container_of(work, struct uwb_rc, + rsv_alien_bp_work.work); + struct uwb_rsv *rsv; + + mutex_lock(&rc->rsvs_mutex); + + list_for_each_entry(rsv, &rc->reservations, rc_node) { + if (rsv->type != UWB_DRP_TYPE_ALIEN_BP) { + rsv->callback(rsv); + } + } + + mutex_unlock(&rc->rsvs_mutex); +} + static void uwb_rsv_timer(unsigned long arg) { struct uwb_rsv *rsv = (struct uwb_rsv *)arg; - rsv->expired = true; - uwb_rsv_sched_update(rsv->rc); + queue_work(rsv->rc->rsv_workq, &rsv->handle_timeout_work); } /** @@ -673,16 +944,27 @@ void uwb_rsv_remove_all(struct uwb_rc *rc) list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) { uwb_rsv_remove(rsv); } + /* Cancel any postponed update. */ + rc->set_drp_ie_pending = 0; mutex_unlock(&rc->rsvs_mutex); - cancel_work_sync(&rc->rsv_update_work); + cancel_delayed_work_sync(&rc->rsv_update_work); } void uwb_rsv_init(struct uwb_rc *rc) { INIT_LIST_HEAD(&rc->reservations); + INIT_LIST_HEAD(&rc->cnflt_alien_list); mutex_init(&rc->rsvs_mutex); - INIT_WORK(&rc->rsv_update_work, uwb_rsv_update_work); + spin_lock_init(&rc->rsvs_lock); + INIT_DELAYED_WORK(&rc->rsv_update_work, uwb_rsv_update_work); + INIT_DELAYED_WORK(&rc->rsv_alien_bp_work, uwb_rsv_alien_bp_work); + rc->bow.can_reserve_extra_mases = true; + rc->bow.total_expired = 0; + rc->bow.window = UWB_DRP_BACKOFF_WIN_MIN >> 1; + init_timer(&rc->bow.timer); + rc->bow.timer.function = uwb_rsv_backoff_win_timer; + rc->bow.timer.data = (unsigned long)&rc->bow; bitmap_complement(rc->uwb_dev.streams, rc->uwb_dev.streams, UWB_NUM_STREAMS); } diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c index a6debb9baf38..89b2e6a7214c 100644 --- a/drivers/uwb/uwb-debug.c +++ b/drivers/uwb/uwb-debug.c @@ -82,29 +82,21 @@ struct uwb_dbg { struct dentry *reservations_f; struct dentry *accept_f; struct dentry *drp_avail_f; + spinlock_t list_lock; }; static struct dentry *root_dir; static void uwb_dbg_rsv_cb(struct uwb_rsv *rsv) { - struct uwb_rc *rc = rsv->rc; - struct device *dev = &rc->uwb_dev.dev; - struct uwb_dev_addr devaddr; - char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE]; - - uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr); - if (rsv->target.type == UWB_RSV_TARGET_DEV) - devaddr = rsv->target.dev->dev_addr; - else - devaddr = rsv->target.devaddr; - uwb_dev_addr_print(target, sizeof(target), &devaddr); + struct uwb_dbg *dbg = rsv->pal_priv; - dev_dbg(dev, "debug: rsv %s -> %s: %s\n", - owner, target, uwb_rsv_state_str(rsv->state)); + uwb_rsv_dump("debug", rsv); if (rsv->state == UWB_RSV_STATE_NONE) { + spin_lock(&dbg->list_lock); list_del(&rsv->pal_node); + spin_unlock(&dbg->list_lock); uwb_rsv_destroy(rsv); } } @@ -128,20 +120,21 @@ static int cmd_rsv_establish(struct uwb_rc *rc, return -ENOMEM; } - rsv->owner = &rc->uwb_dev; - rsv->target.type = UWB_RSV_TARGET_DEV; - rsv->target.dev = target; - rsv->type = cmd->type; - rsv->max_mas = cmd->max_mas; - rsv->min_mas = cmd->min_mas; - rsv->sparsity = cmd->sparsity; + rsv->target.type = UWB_RSV_TARGET_DEV; + rsv->target.dev = target; + rsv->type = cmd->type; + rsv->max_mas = cmd->max_mas; + rsv->min_mas = cmd->min_mas; + rsv->max_interval = cmd->max_interval; ret = uwb_rsv_establish(rsv); if (ret) uwb_rsv_destroy(rsv); - else + else { + spin_lock(&(rc->dbg)->list_lock); list_add_tail(&rsv->pal_node, &rc->dbg->rsvs); - + spin_unlock(&(rc->dbg)->list_lock); + } return ret; } @@ -151,17 +144,24 @@ static int cmd_rsv_terminate(struct uwb_rc *rc, struct uwb_rsv *rsv, *found = NULL; int i = 0; + spin_lock(&(rc->dbg)->list_lock); + list_for_each_entry(rsv, &rc->dbg->rsvs, pal_node) { if (i == cmd->index) { found = rsv; + uwb_rsv_get(found); break; } i++; } + + spin_unlock(&(rc->dbg)->list_lock); + if (!found) return -EINVAL; uwb_rsv_terminate(found); + uwb_rsv_put(found); return 0; } @@ -191,7 +191,7 @@ static ssize_t command_write(struct file *file, const char __user *buf, struct uwb_rc *rc = file->private_data; struct uwb_dbg_cmd cmd; int ret = 0; - + if (len != sizeof(struct uwb_dbg_cmd)) return -EINVAL; @@ -325,7 +325,9 @@ static void uwb_dbg_new_rsv(struct uwb_pal *pal, struct uwb_rsv *rsv) struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal); if (dbg->accept) { + spin_lock(&dbg->list_lock); list_add_tail(&rsv->pal_node, &dbg->rsvs); + spin_unlock(&dbg->list_lock); uwb_rsv_accept(rsv, uwb_dbg_rsv_cb, dbg); } } @@ -341,6 +343,7 @@ void uwb_dbg_add_rc(struct uwb_rc *rc) return; INIT_LIST_HEAD(&rc->dbg->rsvs); + spin_lock_init(&(rc->dbg)->list_lock); uwb_pal_init(&rc->dbg->pal); rc->dbg->pal.rc = rc; diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h index f0f21f406bf0..d5bcfc1c227a 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/uwb/uwb-internal.h @@ -92,6 +92,12 @@ extern const char *uwb_rc_strerror(unsigned code); struct uwb_rc_neh; +extern int uwb_rc_cmd_async(struct uwb_rc *rc, const char *cmd_name, + struct uwb_rccb *cmd, size_t cmd_size, + u8 expected_type, u16 expected_event, + uwb_rc_cmd_cb_f cb, void *arg); + + void uwb_rc_neh_create(struct uwb_rc *rc); void uwb_rc_neh_destroy(struct uwb_rc *rc); @@ -106,7 +112,69 @@ void uwb_rc_neh_put(struct uwb_rc_neh *neh); extern int uwb_est_create(void); extern void uwb_est_destroy(void); +/* + * UWB conflicting alien reservations + */ +struct uwb_cnflt_alien { + struct uwb_rc *rc; + struct list_head rc_node; + struct uwb_mas_bm mas; + struct timer_list timer; + struct work_struct cnflt_update_work; +}; + +enum uwb_uwb_rsv_alloc_result { + UWB_RSV_ALLOC_FOUND = 0, + UWB_RSV_ALLOC_NOT_FOUND, +}; + +enum uwb_rsv_mas_status { + UWB_RSV_MAS_NOT_AVAIL = 1, + UWB_RSV_MAS_SAFE, + UWB_RSV_MAS_UNSAFE, +}; + +struct uwb_rsv_col_set_info { + unsigned char start_col; + unsigned char interval; + unsigned char safe_mas_per_col; + unsigned char unsafe_mas_per_col; +}; + +struct uwb_rsv_col_info { + unsigned char max_avail_safe; + unsigned char max_avail_unsafe; + unsigned char highest_mas[UWB_MAS_PER_ZONE]; + struct uwb_rsv_col_set_info csi; +}; + +struct uwb_rsv_row_info { + unsigned char avail[UWB_MAS_PER_ZONE]; + unsigned char free_rows; + unsigned char used_rows; +}; + +/* + * UWB find allocation + */ +struct uwb_rsv_alloc_info { + unsigned char bm[UWB_MAS_PER_ZONE * UWB_NUM_ZONES]; + struct uwb_rsv_col_info ci[UWB_NUM_ZONES]; + struct uwb_rsv_row_info ri; + struct uwb_mas_bm *not_available; + struct uwb_mas_bm *result; + int min_mas; + int max_mas; + int max_interval; + int total_allocated_mases; + int safe_allocated_mases; + int unsafe_allocated_mases; + int interval; +}; +int uwb_rsv_find_best_allocation(struct uwb_rsv *rsv, struct uwb_mas_bm *available, + struct uwb_mas_bm *result); +void uwb_rsv_handle_drp_avail_change(struct uwb_rc *rc); /* * UWB Events & management daemon */ @@ -254,18 +322,28 @@ void uwb_rsv_init(struct uwb_rc *rc); int uwb_rsv_setup(struct uwb_rc *rc); void uwb_rsv_cleanup(struct uwb_rc *rc); void uwb_rsv_remove_all(struct uwb_rc *rc); +void uwb_rsv_get(struct uwb_rsv *rsv); +void uwb_rsv_put(struct uwb_rsv *rsv); +bool uwb_rsv_has_two_drp_ies(struct uwb_rsv *rsv); +void uwb_rsv_dump(char *text, struct uwb_rsv *rsv); +int uwb_rsv_try_move(struct uwb_rsv *rsv, struct uwb_mas_bm *available); +void uwb_rsv_backoff_win_timer(unsigned long arg); +void uwb_rsv_backoff_win_increment(struct uwb_rc *rc); +int uwb_rsv_status(struct uwb_rsv *rsv); +int uwb_rsv_companion_status(struct uwb_rsv *rsv); void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state); void uwb_rsv_remove(struct uwb_rsv *rsv); struct uwb_rsv *uwb_rsv_find(struct uwb_rc *rc, struct uwb_dev *src, struct uwb_ie_drp *drp_ie); void uwb_rsv_sched_update(struct uwb_rc *rc); +void uwb_rsv_queue_update(struct uwb_rc *rc); -void uwb_drp_handle_timeout(struct uwb_rsv *rsv); int uwb_drp_ie_update(struct uwb_rsv *rsv); void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie); void uwb_drp_avail_init(struct uwb_rc *rc); +void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail); int uwb_drp_avail_reserve_pending(struct uwb_rc *rc, struct uwb_mas_bm *mas); void uwb_drp_avail_reserve(struct uwb_rc *rc, struct uwb_mas_bm *mas); void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas); diff --git a/include/linux/uwb.h b/include/linux/uwb.h index d7ed5201ade6..c02128991ff7 100644 --- a/include/linux/uwb.h +++ b/include/linux/uwb.h @@ -67,6 +67,7 @@ struct uwb_dev { struct uwb_dev_addr dev_addr; int beacon_slot; DECLARE_BITMAP(streams, UWB_NUM_STREAMS); + DECLARE_BITMAP(last_availability_bm, UWB_NUM_MAS); }; #define to_uwb_dev(d) container_of(d, struct uwb_dev, dev) @@ -109,6 +110,9 @@ struct uwbd { */ struct uwb_mas_bm { DECLARE_BITMAP(bm, UWB_NUM_MAS); + DECLARE_BITMAP(unsafe_bm, UWB_NUM_MAS); + int safe; + int unsafe; }; /** @@ -134,14 +138,24 @@ struct uwb_mas_bm { * FIXME: further target states TBD. */ enum uwb_rsv_state { - UWB_RSV_STATE_NONE, + UWB_RSV_STATE_NONE = 0, UWB_RSV_STATE_O_INITIATED, UWB_RSV_STATE_O_PENDING, UWB_RSV_STATE_O_MODIFIED, UWB_RSV_STATE_O_ESTABLISHED, + UWB_RSV_STATE_O_TO_BE_MOVED, + UWB_RSV_STATE_O_MOVE_EXPANDING, + UWB_RSV_STATE_O_MOVE_COMBINING, + UWB_RSV_STATE_O_MOVE_REDUCING, UWB_RSV_STATE_T_ACCEPTED, UWB_RSV_STATE_T_DENIED, + UWB_RSV_STATE_T_CONFLICT, UWB_RSV_STATE_T_PENDING, + UWB_RSV_STATE_T_EXPANDING_ACCEPTED, + UWB_RSV_STATE_T_EXPANDING_CONFLICT, + UWB_RSV_STATE_T_EXPANDING_PENDING, + UWB_RSV_STATE_T_EXPANDING_DENIED, + UWB_RSV_STATE_T_RESIZED, UWB_RSV_STATE_LAST, }; @@ -166,6 +180,12 @@ struct uwb_rsv_target { }; }; +struct uwb_rsv_move { + struct uwb_mas_bm final_mas; + struct uwb_ie_drp *companion_drp_ie; + struct uwb_mas_bm companion_mas; +}; + /* * Number of streams reserved for reservations targeted at DevAddrs. */ @@ -203,6 +223,7 @@ typedef void (*uwb_rsv_cb_f)(struct uwb_rsv *rsv); * * @status: negotiation status * @stream: stream index allocated for this reservation + * @tiebreaker: conflict tiebreaker for this reservation * @mas: reserved MAS * @drp_ie: the DRP IE * @ie_valid: true iff the DRP IE matches the reservation parameters @@ -225,19 +246,22 @@ struct uwb_rsv { enum uwb_drp_type type; int max_mas; int min_mas; - int sparsity; + int max_interval; bool is_multicast; uwb_rsv_cb_f callback; void *pal_priv; enum uwb_rsv_state state; + bool needs_release_companion_mas; u8 stream; + u8 tiebreaker; struct uwb_mas_bm mas; struct uwb_ie_drp *drp_ie; + struct uwb_rsv_move mv; bool ie_valid; struct timer_list timer; - bool expired; + struct work_struct handle_timeout_work; }; static const @@ -279,6 +303,13 @@ struct uwb_drp_avail { bool ie_valid; }; +struct uwb_drp_backoff_win { + u8 window; + u8 n; + int total_expired; + struct timer_list timer; + bool can_reserve_extra_mases; +}; const char *uwb_rsv_state_str(enum uwb_rsv_state state); const char *uwb_rsv_type_str(enum uwb_drp_type type); @@ -294,6 +325,8 @@ void uwb_rsv_terminate(struct uwb_rsv *rsv); void uwb_rsv_accept(struct uwb_rsv *rsv, uwb_rsv_cb_f cb, void *pal_priv); +void uwb_rsv_get_usable_mas(struct uwb_rsv *orig_rsv, struct uwb_mas_bm *mas); + /** * Radio Control Interface instance * @@ -364,12 +397,18 @@ struct uwb_rc { struct uwbd uwbd; + struct uwb_drp_backoff_win bow; struct uwb_drp_avail drp_avail; struct list_head reservations; + struct list_head cnflt_alien_list; + struct uwb_mas_bm cnflt_alien_bitmap; struct mutex rsvs_mutex; + spinlock_t rsvs_lock; struct workqueue_struct *rsv_workq; - struct work_struct rsv_update_work; + struct delayed_work rsv_update_work; + struct delayed_work rsv_alien_bp_work; + int set_drp_ie_pending; struct mutex ies_mutex; struct uwb_rc_cmd_set_ie *ies; size_t ies_capacity; diff --git a/include/linux/uwb/debug-cmd.h b/include/linux/uwb/debug-cmd.h index 07efbe17db53..8da004e25628 100644 --- a/include/linux/uwb/debug-cmd.h +++ b/include/linux/uwb/debug-cmd.h @@ -43,7 +43,7 @@ struct uwb_dbg_cmd_rsv_establish { __u8 type; __u16 max_mas; __u16 min_mas; - __u8 sparsity; + __u8 max_interval; }; struct uwb_dbg_cmd_rsv_terminate { diff --git a/include/linux/uwb/spec.h b/include/linux/uwb/spec.h index a30436ea53aa..b52e44f1bd33 100644 --- a/include/linux/uwb/spec.h +++ b/include/linux/uwb/spec.h @@ -58,6 +58,11 @@ enum { UWB_NUM_ZONES = 16 }; */ #define UWB_MAS_PER_ZONE (UWB_NUM_MAS / UWB_NUM_ZONES) +/* + * Number of MAS required before a row can be considered available. + */ +#define UWB_USABLE_MAS_PER_ROW (UWB_NUM_ZONES - 1) + /* * Number of streams per DRP reservation between a pair of devices. * @@ -93,6 +98,26 @@ enum { UWB_BEACON_SLOT_LENGTH_US = 85 }; */ enum { UWB_MAX_LOST_BEACONS = 3 }; +/* + * mDRPBackOffWinMin + * + * The minimum number of superframes to wait before trying to reserve + * extra MAS. + * + * [ECMA-368] section 17.16 + */ +enum { UWB_DRP_BACKOFF_WIN_MIN = 2 }; + +/* + * mDRPBackOffWinMax + * + * The maximum number of superframes to wait before trying to reserve + * extra MAS. + * + * [ECMA-368] section 17.16 + */ +enum { UWB_DRP_BACKOFF_WIN_MAX = 16 }; + /* * Length of a superframe in microseconds. */ -- cgit v1.2.3 From 29c0177e6a4ac094302bed54a1d4bbb6b740a9ef Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 13 Dec 2008 21:20:25 +1030 Subject: cpumask: change cpumask_scnprintf, cpumask_parse_user, cpulist_parse, and cpulist_scnprintf to take pointers. Impact: change calling convention of existing cpumask APIs Most cpumask functions started with cpus_: these have been replaced by cpumask_ ones which take struct cpumask pointers as expected. These four functions don't have good replacement names; fortunately they're rarely used, so we just change them over. Signed-off-by: Rusty Russell Signed-off-by: Mike Travis Acked-by: Ingo Molnar Cc: paulus@samba.org Cc: mingo@redhat.com Cc: tony.luck@intel.com Cc: ralf@linux-mips.org Cc: Greg Kroah-Hartman Cc: cl@linux-foundation.org Cc: srostedt@redhat.com --- arch/ia64/kernel/topology.c | 2 +- arch/mips/kernel/smp-cmp.c | 4 +- arch/powerpc/platforms/pseries/xics.c | 2 +- arch/x86/kernel/cpu/intel_cacheinfo.c | 4 +- arch/x86/kernel/setup_percpu.c | 2 +- drivers/base/cpu.c | 2 +- drivers/base/node.c | 4 +- drivers/base/topology.c | 4 +- drivers/pci/pci-sysfs.c | 4 +- drivers/pci/probe.c | 4 +- include/linux/cpumask.h | 87 +++++++++++++++++++++++------------ kernel/cpuset.c | 4 +- kernel/irq/proc.c | 4 +- kernel/profile.c | 4 +- kernel/sched.c | 4 +- kernel/sched_stats.h | 2 +- kernel/taskstats.c | 2 +- kernel/trace/trace.c | 4 +- mm/slub.c | 2 +- 19 files changed, 86 insertions(+), 59 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c index c75b914f2d6b..a8d61a3e9a94 100644 --- a/arch/ia64/kernel/topology.c +++ b/arch/ia64/kernel/topology.c @@ -219,7 +219,7 @@ static ssize_t show_shared_cpu_map(struct cache_info *this_leaf, char *buf) cpumask_t shared_cpu_map; cpus_and(shared_cpu_map, this_leaf->shared_cpu_map, cpu_online_map); - len = cpumask_scnprintf(buf, NR_CPUS+1, shared_cpu_map); + len = cpumask_scnprintf(buf, NR_CPUS+1, &shared_cpu_map); len += sprintf(buf+len, "\n"); return len; } diff --git a/arch/mips/kernel/smp-cmp.c b/arch/mips/kernel/smp-cmp.c index 6789c1a12120..f27beca4b26d 100644 --- a/arch/mips/kernel/smp-cmp.c +++ b/arch/mips/kernel/smp-cmp.c @@ -51,10 +51,10 @@ static int __init allowcpus(char *str) int len; cpus_clear(cpu_allow_map); - if (cpulist_parse(str, cpu_allow_map) == 0) { + if (cpulist_parse(str, &cpu_allow_map) == 0) { cpu_set(0, cpu_allow_map); cpus_and(cpu_possible_map, cpu_possible_map, cpu_allow_map); - len = cpulist_scnprintf(buf, sizeof(buf)-1, cpu_possible_map); + len = cpulist_scnprintf(buf, sizeof(buf)-1, &cpu_possible_map); buf[len] = '\0'; pr_debug("Allowable CPUs: %s\n", buf); return 1; diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index e1904774a70f..64d24310ce7e 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -358,7 +358,7 @@ static void xics_set_affinity(unsigned int virq, cpumask_t cpumask) irq_server = get_irq_server(virq, 1); if (irq_server == -1) { char cpulist[128]; - cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask); + cpumask_scnprintf(cpulist, sizeof(cpulist), &cpumask); printk(KERN_WARNING "%s: No online cpus in the mask %s for irq %d\n", __func__, cpulist, virq); diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c index 3f46afbb1cf1..43ea612d3e9d 100644 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c @@ -626,8 +626,8 @@ static ssize_t show_shared_cpu_map_func(struct _cpuid4_info *this_leaf, cpumask_t *mask = &this_leaf->shared_cpu_map; n = type? - cpulist_scnprintf(buf, len-2, *mask): - cpumask_scnprintf(buf, len-2, *mask); + cpulist_scnprintf(buf, len-2, mask) : + cpumask_scnprintf(buf, len-2, mask); buf[n++] = '\n'; buf[n] = '\0'; } diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c index ae0c0d3bb770..1c2084291f97 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c @@ -282,7 +282,7 @@ static void __cpuinit numa_set_cpumask(int cpu, int enable) else cpu_clear(cpu, *mask); - cpulist_scnprintf(buf, sizeof(buf), *mask); + cpulist_scnprintf(buf, sizeof(buf), mask); printk(KERN_DEBUG "%s cpu %d node %d: mask now %s\n", enable? "numa_add_cpu":"numa_remove_cpu", cpu, node, buf); } diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 64f5d54f7edc..4259072f5bd0 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -109,7 +109,7 @@ static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL); */ static ssize_t print_cpus_map(char *buf, cpumask_t *map) { - int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *map); + int n = cpulist_scnprintf(buf, PAGE_SIZE-2, map); buf[n++] = '\n'; buf[n] = '\0'; diff --git a/drivers/base/node.c b/drivers/base/node.c index f5207090885a..91636cd8b6c9 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -30,8 +30,8 @@ static ssize_t node_read_cpumap(struct sys_device *dev, int type, char *buf) BUILD_BUG_ON((NR_CPUS/32 * 9) > (PAGE_SIZE-1)); len = type? - cpulist_scnprintf(buf, PAGE_SIZE-2, *mask): - cpumask_scnprintf(buf, PAGE_SIZE-2, *mask); + cpulist_scnprintf(buf, PAGE_SIZE-2, mask) : + cpumask_scnprintf(buf, PAGE_SIZE-2, mask); buf[len++] = '\n'; buf[len] = '\0'; return len; diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 199cd97e32e6..a8bc1cbcfa7c 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -49,8 +49,8 @@ static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf) if (len > 1) { n = type? - cpulist_scnprintf(buf, len-2, *mask): - cpumask_scnprintf(buf, len-2, *mask); + cpulist_scnprintf(buf, len-2, mask) : + cpumask_scnprintf(buf, len-2, mask); buf[n++] = '\n'; buf[n] = '\0'; } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 5d72866897a8..c88485860a0a 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -74,7 +74,7 @@ static ssize_t local_cpus_show(struct device *dev, int len; mask = pcibus_to_cpumask(to_pci_dev(dev)->bus); - len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask); + len = cpumask_scnprintf(buf, PAGE_SIZE-2, &mask); buf[len++] = '\n'; buf[len] = '\0'; return len; @@ -88,7 +88,7 @@ static ssize_t local_cpulist_show(struct device *dev, int len; mask = pcibus_to_cpumask(to_pci_dev(dev)->bus); - len = cpulist_scnprintf(buf, PAGE_SIZE-2, mask); + len = cpulist_scnprintf(buf, PAGE_SIZE-2, &mask); buf[len++] = '\n'; buf[len] = '\0'; return len; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 003a9b3c293f..5b3f5937ecf5 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -55,8 +55,8 @@ static ssize_t pci_bus_show_cpuaffinity(struct device *dev, cpumask = pcibus_to_cpumask(to_pci_bus(dev)); ret = type? - cpulist_scnprintf(buf, PAGE_SIZE-2, cpumask): - cpumask_scnprintf(buf, PAGE_SIZE-2, cpumask); + cpulist_scnprintf(buf, PAGE_SIZE-2, &cpumask) : + cpumask_scnprintf(buf, PAGE_SIZE-2, &cpumask); buf[ret++] = '\n'; buf[ret] = '\0'; return ret; diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 21e1dd43e52a..94a2ab88ae85 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -339,36 +339,6 @@ extern cpumask_t cpu_mask_all; #endif #define CPUMASK_PTR(v, m) cpumask_t *v = &(m->v) -#define cpumask_scnprintf(buf, len, src) \ - __cpumask_scnprintf((buf), (len), &(src), NR_CPUS) -static inline int __cpumask_scnprintf(char *buf, int len, - const cpumask_t *srcp, int nbits) -{ - return bitmap_scnprintf(buf, len, srcp->bits, nbits); -} - -#define cpumask_parse_user(ubuf, ulen, dst) \ - __cpumask_parse_user((ubuf), (ulen), &(dst), NR_CPUS) -static inline int __cpumask_parse_user(const char __user *buf, int len, - cpumask_t *dstp, int nbits) -{ - return bitmap_parse_user(buf, len, dstp->bits, nbits); -} - -#define cpulist_scnprintf(buf, len, src) \ - __cpulist_scnprintf((buf), (len), &(src), NR_CPUS) -static inline int __cpulist_scnprintf(char *buf, int len, - const cpumask_t *srcp, int nbits) -{ - return bitmap_scnlistprintf(buf, len, srcp->bits, nbits); -} - -#define cpulist_parse(buf, dst) __cpulist_parse((buf), &(dst), NR_CPUS) -static inline int __cpulist_parse(const char *buf, cpumask_t *dstp, int nbits) -{ - return bitmap_parselist(buf, dstp->bits, nbits); -} - #define cpu_remap(oldbit, old, new) \ __cpu_remap((oldbit), &(old), &(new), NR_CPUS) static inline int __cpu_remap(int oldbit, @@ -945,6 +915,63 @@ static inline void cpumask_copy(struct cpumask *dstp, */ #define cpumask_of(cpu) (get_cpu_mask(cpu)) +/** + * cpumask_scnprintf - print a cpumask into a string as comma-separated hex + * @buf: the buffer to sprintf into + * @len: the length of the buffer + * @srcp: the cpumask to print + * + * If len is zero, returns zero. Otherwise returns the length of the + * (nul-terminated) @buf string. + */ +static inline int cpumask_scnprintf(char *buf, int len, + const struct cpumask *srcp) +{ + return bitmap_scnprintf(buf, len, srcp->bits, nr_cpumask_bits); +} + +/** + * cpumask_parse_user - extract a cpumask from a user string + * @buf: the buffer to extract from + * @len: the length of the buffer + * @dstp: the cpumask to set. + * + * Returns -errno, or 0 for success. + */ +static inline int cpumask_parse_user(const char __user *buf, int len, + struct cpumask *dstp) +{ + return bitmap_parse_user(buf, len, dstp->bits, nr_cpumask_bits); +} + +/** + * cpulist_scnprintf - print a cpumask into a string as comma-separated list + * @buf: the buffer to sprintf into + * @len: the length of the buffer + * @srcp: the cpumask to print + * + * If len is zero, returns zero. Otherwise returns the length of the + * (nul-terminated) @buf string. + */ +static inline int cpulist_scnprintf(char *buf, int len, + const struct cpumask *srcp) +{ + return bitmap_scnlistprintf(buf, len, srcp->bits, nr_cpumask_bits); +} + +/** + * cpulist_parse_user - extract a cpumask from a user string of ranges + * @buf: the buffer to extract from + * @len: the length of the buffer + * @dstp: the cpumask to set. + * + * Returns -errno, or 0 for success. + */ +static inline int cpulist_parse(const char *buf, struct cpumask *dstp) +{ + return bitmap_parselist(buf, dstp->bits, nr_cpumask_bits); +} + /** * to_cpumask - convert an NR_CPUS bitmap to a struct cpumask * * @bitmap: the bitmap diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 96c0ba13b8cd..39c1a4c1c5a9 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -896,7 +896,7 @@ static int update_cpumask(struct cpuset *cs, const char *buf) if (!*buf) { cpus_clear(trialcs.cpus_allowed); } else { - retval = cpulist_parse(buf, trialcs.cpus_allowed); + retval = cpulist_parse(buf, &trialcs.cpus_allowed); if (retval < 0) return retval; @@ -1482,7 +1482,7 @@ static int cpuset_sprintf_cpulist(char *page, struct cpuset *cs) mask = cs->cpus_allowed; mutex_unlock(&callback_mutex); - return cpulist_scnprintf(page, PAGE_SIZE, mask); + return cpulist_scnprintf(page, PAGE_SIZE, &mask); } static int cpuset_sprintf_memlist(char *page, struct cpuset *cs) diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index d257e7d6a8a4..f293349d49d0 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -47,7 +47,7 @@ static ssize_t irq_affinity_proc_write(struct file *file, irq_balancing_disabled(irq)) return -EIO; - err = cpumask_parse_user(buffer, count, new_value); + err = cpumask_parse_user(buffer, count, &new_value); if (err) return err; @@ -95,7 +95,7 @@ static ssize_t default_affinity_write(struct file *file, cpumask_t new_value; int err; - err = cpumask_parse_user(buffer, count, new_value); + err = cpumask_parse_user(buffer, count, &new_value); if (err) return err; diff --git a/kernel/profile.c b/kernel/profile.c index dc41827fbfee..7d620dfdde59 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -442,7 +442,7 @@ void profile_tick(int type) static int prof_cpu_mask_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { - int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); + int len = cpumask_scnprintf(page, count, (cpumask_t *)data); if (count - len < 2) return -EINVAL; len += sprintf(page + len, "\n"); @@ -456,7 +456,7 @@ static int prof_cpu_mask_write_proc(struct file *file, unsigned long full_count = count, err; cpumask_t new_value; - err = cpumask_parse_user(buffer, count, new_value); + err = cpumask_parse_user(buffer, count, &new_value); if (err) return err; diff --git a/kernel/sched.c b/kernel/sched.c index e4bb1dd7b308..d2d16d1273b1 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -6666,7 +6666,7 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level, struct sched_group *group = sd->groups; char str[256]; - cpulist_scnprintf(str, sizeof(str), sd->span); + cpulist_scnprintf(str, sizeof(str), &sd->span); cpus_clear(*groupmask); printk(KERN_DEBUG "%*s domain %d: ", level, "", level); @@ -6720,7 +6720,7 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level, cpus_or(*groupmask, *groupmask, group->cpumask); - cpulist_scnprintf(str, sizeof(str), group->cpumask); + cpulist_scnprintf(str, sizeof(str), &group->cpumask); printk(KERN_CONT " %s", str); group = group->next; diff --git a/kernel/sched_stats.h b/kernel/sched_stats.h index 7dbf72a2b02c..6beff1e4eeae 100644 --- a/kernel/sched_stats.h +++ b/kernel/sched_stats.h @@ -42,7 +42,7 @@ static int show_schedstat(struct seq_file *seq, void *v) for_each_domain(cpu, sd) { enum cpu_idle_type itype; - cpumask_scnprintf(mask_str, mask_len, sd->span); + cpumask_scnprintf(mask_str, mask_len, &sd->span); seq_printf(seq, "domain%d %s", dcount++, mask_str); for (itype = CPU_IDLE; itype < CPU_MAX_IDLE_TYPES; itype++) { diff --git a/kernel/taskstats.c b/kernel/taskstats.c index bd6be76303cf..6d7dc4ec4aa5 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -352,7 +352,7 @@ static int parse(struct nlattr *na, cpumask_t *mask) if (!data) return -ENOMEM; nla_strlcpy(data, na, len); - ret = cpulist_parse(data, *mask); + ret = cpulist_parse(data, mask); kfree(data); return ret; } diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index d86e3252f300..d2e75479dc50 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2126,7 +2126,7 @@ tracing_cpumask_read(struct file *filp, char __user *ubuf, mutex_lock(&tracing_cpumask_update_lock); - len = cpumask_scnprintf(mask_str, count, tracing_cpumask); + len = cpumask_scnprintf(mask_str, count, &tracing_cpumask); if (count - len < 2) { count = -EINVAL; goto out_err; @@ -2147,7 +2147,7 @@ tracing_cpumask_write(struct file *filp, const char __user *ubuf, int err, cpu; mutex_lock(&tracing_cpumask_update_lock); - err = cpumask_parse_user(ubuf, count, tracing_cpumask_new); + err = cpumask_parse_user(ubuf, count, &tracing_cpumask_new); if (err) goto err_unlock; diff --git a/mm/slub.c b/mm/slub.c index a2cd47d89e0a..8e516e29f989 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -3626,7 +3626,7 @@ static int list_locations(struct kmem_cache *s, char *buf, len < PAGE_SIZE - 60) { len += sprintf(buf + len, " cpus="); len += cpulist_scnprintf(buf + len, PAGE_SIZE - len - 50, - l->cpus); + &l->cpus); } if (num_online_nodes() > 1 && !nodes_empty(l->nodes) && -- cgit v1.2.3 From 0de26520c7cabf36e1de090ea8092f011a6106ce Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 13 Dec 2008 21:20:26 +1030 Subject: cpumask: make irq_set_affinity() take a const struct cpumask Impact: change existing irq_chip API Not much point with gentle transition here: the struct irq_chip's setaffinity method signature needs to change. Fortunately, not widely used code, but hits a few architectures. Note: In irq_select_affinity() I save a temporary in by mangling irq_desc[irq].affinity directly. Ingo, does this break anything? (Folded in fix from KOSAKI Motohiro) Signed-off-by: Rusty Russell Signed-off-by: Mike Travis Reviewed-by: Grant Grundler Acked-by: Ingo Molnar Cc: ralf@linux-mips.org Cc: grundler@parisc-linux.org Cc: jeremy@xensource.com Cc: KOSAKI Motohiro --- arch/alpha/kernel/irq.c | 2 +- arch/alpha/kernel/sys_dp264.c | 8 ++-- arch/alpha/kernel/sys_titan.c | 4 +- arch/arm/common/gic.c | 4 +- arch/arm/kernel/irq.c | 2 +- arch/arm/oprofile/op_model_mpcore.c | 4 +- arch/cris/arch-v32/kernel/irq.c | 4 +- arch/ia64/hp/sim/hpsim_irq.c | 2 +- arch/ia64/kernel/iosapic.c | 12 +++--- arch/ia64/kernel/irq.c | 9 ++-- arch/ia64/kernel/msi_ia64.c | 12 +++--- arch/ia64/kernel/smpboot.c | 4 +- arch/ia64/sn/kernel/irq.c | 6 +-- arch/ia64/sn/kernel/msi_sn.c | 7 +-- arch/mips/include/asm/irq.h | 3 +- arch/mips/kernel/cevt-bcm1480.c | 2 +- arch/mips/kernel/cevt-sb1250.c | 2 +- arch/mips/kernel/irq-gic.c | 6 +-- arch/mips/mti-malta/malta-smtc.c | 6 +-- arch/mips/sibyte/bcm1480/irq.c | 8 ++-- arch/mips/sibyte/sb1250/irq.c | 8 ++-- arch/parisc/kernel/irq.c | 6 +-- arch/powerpc/kernel/irq.c | 2 +- arch/powerpc/platforms/pseries/xics.c | 6 +-- arch/powerpc/sysdev/mpic.c | 4 +- arch/powerpc/sysdev/mpic.h | 2 +- arch/sparc64/kernel/irq.c | 11 +++-- arch/sparc64/kernel/of_device.c | 2 +- arch/sparc64/kernel/pci_msi.c | 2 +- arch/x86/kernel/hpet.c | 4 +- arch/x86/kernel/io_apic.c | 81 +++++++++++++++++------------------ arch/x86/kernel/irq_32.c | 2 +- arch/x86/kernel/irq_64.c | 2 +- drivers/parisc/iosapic.c | 7 +-- drivers/xen/events.c | 6 +-- include/linux/interrupt.h | 4 +- include/linux/irq.h | 3 +- kernel/irq/chip.c | 2 +- kernel/irq/manage.c | 22 +++++----- kernel/irq/migration.c | 14 +++--- kernel/irq/proc.c | 29 ++++++++----- kernel/time/tick-common.c | 6 +-- 42 files changed, 171 insertions(+), 161 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index c626a821cdcb..d0f1620007f7 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -55,7 +55,7 @@ int irq_select_affinity(unsigned int irq) last_cpu = cpu; irq_desc[irq].affinity = cpumask_of_cpu(cpu); - irq_desc[irq].chip->set_affinity(irq, cpumask_of_cpu(cpu)); + irq_desc[irq].chip->set_affinity(irq, cpumask_of(cpu)); return 0; } #endif /* CONFIG_SMP */ diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index c71b0fd7a61f..ab44c164d9d4 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -177,19 +177,19 @@ cpu_set_irq_affinity(unsigned int irq, cpumask_t affinity) } static void -dp264_set_affinity(unsigned int irq, cpumask_t affinity) +dp264_set_affinity(unsigned int irq, const struct cpumask *affinity) { spin_lock(&dp264_irq_lock); - cpu_set_irq_affinity(irq, affinity); + cpu_set_irq_affinity(irq, *affinity); tsunami_update_irq_hw(cached_irq_mask); spin_unlock(&dp264_irq_lock); } static void -clipper_set_affinity(unsigned int irq, cpumask_t affinity) +clipper_set_affinity(unsigned int irq, const struct cpumask *affinity) { spin_lock(&dp264_irq_lock); - cpu_set_irq_affinity(irq - 16, affinity); + cpu_set_irq_affinity(irq - 16, *affinity); tsunami_update_irq_hw(cached_irq_mask); spin_unlock(&dp264_irq_lock); } diff --git a/arch/alpha/kernel/sys_titan.c b/arch/alpha/kernel/sys_titan.c index 52c91ccc1648..27f840a4ad3d 100644 --- a/arch/alpha/kernel/sys_titan.c +++ b/arch/alpha/kernel/sys_titan.c @@ -158,10 +158,10 @@ titan_cpu_set_irq_affinity(unsigned int irq, cpumask_t affinity) } static void -titan_set_irq_affinity(unsigned int irq, cpumask_t affinity) +titan_set_irq_affinity(unsigned int irq, const struct cpumask *affinity) { spin_lock(&titan_irq_lock); - titan_cpu_set_irq_affinity(irq - 16, affinity); + titan_cpu_set_irq_affinity(irq - 16, *affinity); titan_update_irq_hw(titan_cached_irq_mask); spin_unlock(&titan_irq_lock); } diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 7fc9860a97d7..c6884ba1d5ed 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -109,11 +109,11 @@ static void gic_unmask_irq(unsigned int irq) } #ifdef CONFIG_SMP -static void gic_set_cpu(unsigned int irq, cpumask_t mask_val) +static void gic_set_cpu(unsigned int irq, const struct cpumask *mask_val) { void __iomem *reg = gic_dist_base(irq) + GIC_DIST_TARGET + (gic_irq(irq) & ~3); unsigned int shift = (irq % 4) * 8; - unsigned int cpu = first_cpu(mask_val); + unsigned int cpu = cpumask_first(mask_val); u32 val; spin_lock(&irq_controller_lock); diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 2f3eb795fa6e..7141cee1fab7 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -174,7 +174,7 @@ static void route_irq(struct irq_desc *desc, unsigned int irq, unsigned int cpu) pr_debug("IRQ%u: moving from cpu%u to cpu%u\n", irq, desc->cpu, cpu); spin_lock_irq(&desc->lock); - desc->chip->set_affinity(irq, cpumask_of_cpu(cpu)); + desc->chip->set_affinity(irq, cpumask_of(cpu)); spin_unlock_irq(&desc->lock); } diff --git a/arch/arm/oprofile/op_model_mpcore.c b/arch/arm/oprofile/op_model_mpcore.c index 4de366e8b4c5..6d6bd5899240 100644 --- a/arch/arm/oprofile/op_model_mpcore.c +++ b/arch/arm/oprofile/op_model_mpcore.c @@ -260,10 +260,10 @@ static void em_stop(void) static void em_route_irq(int irq, unsigned int cpu) { struct irq_desc *desc = irq_desc + irq; - cpumask_t mask = cpumask_of_cpu(cpu); + const struct cpumask *mask = cpumask_of(cpu); spin_lock_irq(&desc->lock); - desc->affinity = mask; + desc->affinity = *mask; desc->chip->set_affinity(irq, mask); spin_unlock_irq(&desc->lock); } diff --git a/arch/cris/arch-v32/kernel/irq.c b/arch/cris/arch-v32/kernel/irq.c index 173c141ac9ba..295131fee710 100644 --- a/arch/cris/arch-v32/kernel/irq.c +++ b/arch/cris/arch-v32/kernel/irq.c @@ -325,11 +325,11 @@ static void end_crisv32_irq(unsigned int irq) { } -void set_affinity_crisv32_irq(unsigned int irq, cpumask_t dest) +void set_affinity_crisv32_irq(unsigned int irq, const struct cpumask *dest) { unsigned long flags; spin_lock_irqsave(&irq_lock, flags); - irq_allocations[irq - FIRST_IRQ].mask = dest; + irq_allocations[irq - FIRST_IRQ].mask = *dest; spin_unlock_irqrestore(&irq_lock, flags); } diff --git a/arch/ia64/hp/sim/hpsim_irq.c b/arch/ia64/hp/sim/hpsim_irq.c index c2f58ff364e7..cc0a3182db3c 100644 --- a/arch/ia64/hp/sim/hpsim_irq.c +++ b/arch/ia64/hp/sim/hpsim_irq.c @@ -22,7 +22,7 @@ hpsim_irq_noop (unsigned int irq) } static void -hpsim_set_affinity_noop (unsigned int a, cpumask_t b) +hpsim_set_affinity_noop(unsigned int a, const struct cpumask *b) { } diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c index 5c4674ae8aea..c8adecd5b416 100644 --- a/arch/ia64/kernel/iosapic.c +++ b/arch/ia64/kernel/iosapic.c @@ -330,25 +330,25 @@ unmask_irq (unsigned int irq) static void -iosapic_set_affinity (unsigned int irq, cpumask_t mask) +iosapic_set_affinity(unsigned int irq, const struct cpumask *mask) { #ifdef CONFIG_SMP u32 high32, low32; - int dest, rte_index; + int cpu, dest, rte_index; int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0; struct iosapic_rte_info *rte; struct iosapic *iosapic; irq &= (~IA64_IRQ_REDIRECTED); - cpus_and(mask, mask, cpu_online_map); - if (cpus_empty(mask)) + cpu = cpumask_first_and(cpu_online_mask, mask); + if (cpu >= nr_cpu_ids) return; - if (irq_prepare_move(irq, first_cpu(mask))) + if (irq_prepare_move(irq, cpu)) return; - dest = cpu_physical_id(first_cpu(mask)); + dest = cpu_physical_id(cpu); if (!iosapic_intr_info[irq].count) return; /* not an IOSAPIC interrupt */ diff --git a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c index 7fd18f54c056..0b6db53fedcf 100644 --- a/arch/ia64/kernel/irq.c +++ b/arch/ia64/kernel/irq.c @@ -133,7 +133,6 @@ unsigned int vectors_in_migration[NR_IRQS]; */ static void migrate_irqs(void) { - cpumask_t mask; irq_desc_t *desc; int irq, new_cpu; @@ -152,15 +151,14 @@ static void migrate_irqs(void) if (desc->status == IRQ_PER_CPU) continue; - cpus_and(mask, irq_desc[irq].affinity, cpu_online_map); - if (any_online_cpu(mask) == NR_CPUS) { + if (cpumask_any_and(&irq_desc[irq].affinity, cpu_online_mask) + >= nr_cpu_ids) { /* * Save it for phase 2 processing */ vectors_in_migration[irq] = irq; new_cpu = any_online_cpu(cpu_online_map); - mask = cpumask_of_cpu(new_cpu); /* * Al three are essential, currently WARN_ON.. maybe panic? @@ -168,7 +166,8 @@ static void migrate_irqs(void) if (desc->chip && desc->chip->disable && desc->chip->enable && desc->chip->set_affinity) { desc->chip->disable(irq); - desc->chip->set_affinity(irq, mask); + desc->chip->set_affinity(irq, + cpumask_of(new_cpu)); desc->chip->enable(irq); } else { WARN_ON((!(desc->chip) || !(desc->chip->disable) || diff --git a/arch/ia64/kernel/msi_ia64.c b/arch/ia64/kernel/msi_ia64.c index 702a09c13238..890339339035 100644 --- a/arch/ia64/kernel/msi_ia64.c +++ b/arch/ia64/kernel/msi_ia64.c @@ -49,11 +49,12 @@ static struct irq_chip ia64_msi_chip; #ifdef CONFIG_SMP -static void ia64_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) +static void ia64_set_msi_irq_affinity(unsigned int irq, + const cpumask_t *cpu_mask) { struct msi_msg msg; u32 addr, data; - int cpu = first_cpu(cpu_mask); + int cpu = first_cpu(*cpu_mask); if (!cpu_online(cpu)) return; @@ -166,12 +167,11 @@ void arch_teardown_msi_irq(unsigned int irq) #ifdef CONFIG_DMAR #ifdef CONFIG_SMP -static void dmar_msi_set_affinity(unsigned int irq, cpumask_t mask) +static void dmar_msi_set_affinity(unsigned int irq, const struct cpumask *mask) { struct irq_cfg *cfg = irq_cfg + irq; struct msi_msg msg; - int cpu = first_cpu(mask); - + int cpu = cpumask_first(mask); if (!cpu_online(cpu)) return; @@ -187,7 +187,7 @@ static void dmar_msi_set_affinity(unsigned int irq, cpumask_t mask) msg.address_lo |= MSI_ADDR_DESTID_CPU(cpu_physical_id(cpu)); dmar_msi_write(irq, &msg); - irq_desc[irq].affinity = mask; + irq_desc[irq].affinity = *mask; } #endif /* CONFIG_SMP */ diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index 4ede6e571c38..11463994a7d5 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c @@ -682,7 +682,7 @@ int migrate_platform_irqs(unsigned int cpu) { int new_cpei_cpu; irq_desc_t *desc = NULL; - cpumask_t mask; + const struct cpumask *mask; int retval = 0; /* @@ -695,7 +695,7 @@ int migrate_platform_irqs(unsigned int cpu) * Now re-target the CPEI to a different processor */ new_cpei_cpu = any_online_cpu(cpu_online_map); - mask = cpumask_of_cpu(new_cpei_cpu); + mask = cpumask_of(new_cpei_cpu); set_cpei_target_cpu(new_cpei_cpu); desc = irq_desc + ia64_cpe_irq; /* diff --git a/arch/ia64/sn/kernel/irq.c b/arch/ia64/sn/kernel/irq.c index 0c66dbdd1d72..66fd705e82c0 100644 --- a/arch/ia64/sn/kernel/irq.c +++ b/arch/ia64/sn/kernel/irq.c @@ -227,14 +227,14 @@ finish_up: return new_irq_info; } -static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask) +static void sn_set_affinity_irq(unsigned int irq, const struct cpumask *mask) { struct sn_irq_info *sn_irq_info, *sn_irq_info_safe; nasid_t nasid; int slice; - nasid = cpuid_to_nasid(first_cpu(mask)); - slice = cpuid_to_slice(first_cpu(mask)); + nasid = cpuid_to_nasid(cpumask_first(mask)); + slice = cpuid_to_slice(cpumask_first(mask)); list_for_each_entry_safe(sn_irq_info, sn_irq_info_safe, sn_irq_lh[irq], list) diff --git a/arch/ia64/sn/kernel/msi_sn.c b/arch/ia64/sn/kernel/msi_sn.c index 83f190ffe350..ca553b0429ce 100644 --- a/arch/ia64/sn/kernel/msi_sn.c +++ b/arch/ia64/sn/kernel/msi_sn.c @@ -151,7 +151,8 @@ int sn_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *entry) } #ifdef CONFIG_SMP -static void sn_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) +static void sn_set_msi_irq_affinity(unsigned int irq, + const struct cpumask *cpu_mask) { struct msi_msg msg; int slice; @@ -164,7 +165,7 @@ static void sn_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) struct sn_pcibus_provider *provider; unsigned int cpu; - cpu = first_cpu(cpu_mask); + cpu = cpumask_first(cpu_mask); sn_irq_info = sn_msi_info[irq].sn_irq_info; if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0) return; @@ -204,7 +205,7 @@ static void sn_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff); write_msi_msg(irq, &msg); - irq_desc[irq].affinity = cpu_mask; + irq_desc[irq].affinity = *cpu_mask; } #endif /* CONFIG_SMP */ diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h index a58f0eecc68f..abc62aa744ac 100644 --- a/arch/mips/include/asm/irq.h +++ b/arch/mips/include/asm/irq.h @@ -49,7 +49,8 @@ static inline void smtc_im_ack_irq(unsigned int irq) #ifdef CONFIG_MIPS_MT_SMTC_IRQAFF #include -extern void plat_set_irq_affinity(unsigned int irq, cpumask_t affinity); +extern void plat_set_irq_affinity(unsigned int irq, + const struct cpumask *affinity); extern void smtc_forward_irq(unsigned int irq); /* diff --git a/arch/mips/kernel/cevt-bcm1480.c b/arch/mips/kernel/cevt-bcm1480.c index 0a57f86945f1..d7e21bc8cd21 100644 --- a/arch/mips/kernel/cevt-bcm1480.c +++ b/arch/mips/kernel/cevt-bcm1480.c @@ -148,6 +148,6 @@ void __cpuinit sb1480_clockevent_init(void) action->name = name; action->dev_id = cd; - irq_set_affinity(irq, cpumask_of_cpu(cpu)); + irq_set_affinity(irq, cpumask_of(cpu)); setup_irq(irq, action); } diff --git a/arch/mips/kernel/cevt-sb1250.c b/arch/mips/kernel/cevt-sb1250.c index 63ac3ad462bc..0f188cd46e03 100644 --- a/arch/mips/kernel/cevt-sb1250.c +++ b/arch/mips/kernel/cevt-sb1250.c @@ -147,6 +147,6 @@ void __cpuinit sb1250_clockevent_init(void) action->name = name; action->dev_id = cd; - irq_set_affinity(irq, cpumask_of_cpu(cpu)); + irq_set_affinity(irq, cpumask_of(cpu)); setup_irq(irq, action); } diff --git a/arch/mips/kernel/irq-gic.c b/arch/mips/kernel/irq-gic.c index f0a4bb19e096..494a49a317e9 100644 --- a/arch/mips/kernel/irq-gic.c +++ b/arch/mips/kernel/irq-gic.c @@ -155,7 +155,7 @@ static void gic_unmask_irq(unsigned int irq) static DEFINE_SPINLOCK(gic_lock); -static void gic_set_affinity(unsigned int irq, cpumask_t cpumask) +static void gic_set_affinity(unsigned int irq, const struct cpumask *cpumask) { cpumask_t tmp = CPU_MASK_NONE; unsigned long flags; @@ -164,7 +164,7 @@ static void gic_set_affinity(unsigned int irq, cpumask_t cpumask) pr_debug(KERN_DEBUG "%s called\n", __func__); irq -= _irqbase; - cpus_and(tmp, cpumask, cpu_online_map); + cpumask_and(&tmp, cpumask, cpu_online_mask); if (cpus_empty(tmp)) return; @@ -187,7 +187,7 @@ static void gic_set_affinity(unsigned int irq, cpumask_t cpumask) set_bit(irq, pcpu_masks[first_cpu(tmp)].pcpu_mask); } - irq_desc[irq].affinity = cpumask; + irq_desc[irq].affinity = *cpumask; spin_unlock_irqrestore(&gic_lock, flags); } diff --git a/arch/mips/mti-malta/malta-smtc.c b/arch/mips/mti-malta/malta-smtc.c index f84a46a8ae6e..aabd7274507b 100644 --- a/arch/mips/mti-malta/malta-smtc.c +++ b/arch/mips/mti-malta/malta-smtc.c @@ -114,9 +114,9 @@ struct plat_smp_ops msmtc_smp_ops = { */ -void plat_set_irq_affinity(unsigned int irq, cpumask_t affinity) +void plat_set_irq_affinity(unsigned int irq, const struct cpumask *affinity) { - cpumask_t tmask = affinity; + cpumask_t tmask = *affinity; int cpu = 0; void smtc_set_irq_affinity(unsigned int irq, cpumask_t aff); @@ -139,7 +139,7 @@ void plat_set_irq_affinity(unsigned int irq, cpumask_t affinity) * be made to forward to an offline "CPU". */ - for_each_cpu_mask(cpu, affinity) { + for_each_cpu(cpu, affinity) { if ((cpu_data[cpu].vpe_id != 0) || !cpu_online(cpu)) cpu_clear(cpu, tmask); } diff --git a/arch/mips/sibyte/bcm1480/irq.c b/arch/mips/sibyte/bcm1480/irq.c index a35818ed4263..12b465d404df 100644 --- a/arch/mips/sibyte/bcm1480/irq.c +++ b/arch/mips/sibyte/bcm1480/irq.c @@ -50,7 +50,7 @@ static void enable_bcm1480_irq(unsigned int irq); static void disable_bcm1480_irq(unsigned int irq); static void ack_bcm1480_irq(unsigned int irq); #ifdef CONFIG_SMP -static void bcm1480_set_affinity(unsigned int irq, cpumask_t mask); +static void bcm1480_set_affinity(unsigned int irq, const struct cpumask *mask); #endif #ifdef CONFIG_PCI @@ -109,7 +109,7 @@ void bcm1480_unmask_irq(int cpu, int irq) } #ifdef CONFIG_SMP -static void bcm1480_set_affinity(unsigned int irq, cpumask_t mask) +static void bcm1480_set_affinity(unsigned int irq, const struct cpumask *mask) { int i = 0, old_cpu, cpu, int_on, k; u64 cur_ints; @@ -117,11 +117,11 @@ static void bcm1480_set_affinity(unsigned int irq, cpumask_t mask) unsigned long flags; unsigned int irq_dirty; - if (cpus_weight(mask) != 1) { + if (cpumask_weight(mask) != 1) { printk("attempted to set irq affinity for irq %d to multiple CPUs\n", irq); return; } - i = first_cpu(mask); + i = cpumask_first(mask); /* Convert logical CPU to physical CPU */ cpu = cpu_logical_map(i); diff --git a/arch/mips/sibyte/sb1250/irq.c b/arch/mips/sibyte/sb1250/irq.c index a5158483986e..808ac2959b8c 100644 --- a/arch/mips/sibyte/sb1250/irq.c +++ b/arch/mips/sibyte/sb1250/irq.c @@ -50,7 +50,7 @@ static void enable_sb1250_irq(unsigned int irq); static void disable_sb1250_irq(unsigned int irq); static void ack_sb1250_irq(unsigned int irq); #ifdef CONFIG_SMP -static void sb1250_set_affinity(unsigned int irq, cpumask_t mask); +static void sb1250_set_affinity(unsigned int irq, const struct cpumask *mask); #endif #ifdef CONFIG_SIBYTE_HAS_LDT @@ -103,16 +103,16 @@ void sb1250_unmask_irq(int cpu, int irq) } #ifdef CONFIG_SMP -static void sb1250_set_affinity(unsigned int irq, cpumask_t mask) +static void sb1250_set_affinity(unsigned int irq, const struct cpumask *mask) { int i = 0, old_cpu, cpu, int_on; u64 cur_ints; struct irq_desc *desc = irq_desc + irq; unsigned long flags; - i = first_cpu(mask); + i = cpumask_first(mask); - if (cpus_weight(mask) > 1) { + if (cpumask_weight(mask) > 1) { printk("attempted to set irq affinity for irq %d to multiple CPUs\n", irq); return; } diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c index 23ef950df008..4cea935e2f99 100644 --- a/arch/parisc/kernel/irq.c +++ b/arch/parisc/kernel/irq.c @@ -131,12 +131,12 @@ int cpu_check_affinity(unsigned int irq, cpumask_t *dest) return 0; } -static void cpu_set_affinity_irq(unsigned int irq, cpumask_t dest) +static void cpu_set_affinity_irq(unsigned int irq, const struct cpumask *dest) { - if (cpu_check_affinity(irq, &dest)) + if (cpu_check_affinity(irq, dest)) return; - irq_desc[irq].affinity = dest; + irq_desc[irq].affinity = *dest; } #endif diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index ac222d0ab12e..23b8b5e36f98 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -237,7 +237,7 @@ void fixup_irqs(cpumask_t map) mask = map; } if (irq_desc[irq].chip->set_affinity) - irq_desc[irq].chip->set_affinity(irq, mask); + irq_desc[irq].chip->set_affinity(irq, &mask); else if (irq_desc[irq].action && !(warned++)) printk("Cannot set affinity for irq %i\n", irq); } diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 64d24310ce7e..424b335a71c8 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -332,7 +332,7 @@ static void xics_eoi_lpar(unsigned int virq) lpar_xirr_info_set((0xff << 24) | irq); } -static void xics_set_affinity(unsigned int virq, cpumask_t cpumask) +static void xics_set_affinity(unsigned int virq, const struct cpumask *cpumask) { unsigned int irq; int status; @@ -358,7 +358,7 @@ static void xics_set_affinity(unsigned int virq, cpumask_t cpumask) irq_server = get_irq_server(virq, 1); if (irq_server == -1) { char cpulist[128]; - cpumask_scnprintf(cpulist, sizeof(cpulist), &cpumask); + cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask); printk(KERN_WARNING "%s: No online cpus in the mask %s for irq %d\n", __func__, cpulist, virq); @@ -845,7 +845,7 @@ void xics_migrate_irqs_away(void) /* Reset affinity to all cpus */ irq_desc[virq].affinity = CPU_MASK_ALL; - desc->chip->set_affinity(virq, CPU_MASK_ALL); + desc->chip->set_affinity(virq, cpu_all_mask); unlock: spin_unlock_irqrestore(&desc->lock, flags); } diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 1890fb085cde..5d7f9f0c93c3 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -817,7 +817,7 @@ static void mpic_end_ipi(unsigned int irq) #endif /* CONFIG_SMP */ -void mpic_set_affinity(unsigned int irq, cpumask_t cpumask) +void mpic_set_affinity(unsigned int irq, const struct cpumask *cpumask) { struct mpic *mpic = mpic_from_irq(irq); unsigned int src = mpic_irq_to_hw(irq); @@ -829,7 +829,7 @@ void mpic_set_affinity(unsigned int irq, cpumask_t cpumask) } else { cpumask_t tmp; - cpus_and(tmp, cpumask, cpu_online_map); + cpumask_and(&tmp, cpumask, cpu_online_mask); mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), mpic_physmask(cpus_addr(tmp)[0])); diff --git a/arch/powerpc/sysdev/mpic.h b/arch/powerpc/sysdev/mpic.h index 6209c62a426d..3cef2af10f42 100644 --- a/arch/powerpc/sysdev/mpic.h +++ b/arch/powerpc/sysdev/mpic.h @@ -36,6 +36,6 @@ static inline int mpic_pasemi_msi_init(struct mpic *mpic) extern int mpic_set_irq_type(unsigned int virq, unsigned int flow_type); extern void mpic_set_vector(unsigned int virq, unsigned int vector); -extern void mpic_set_affinity(unsigned int irq, cpumask_t cpumask); +extern void mpic_set_affinity(unsigned int irq, const struct cpumask *cpumask); #endif /* _POWERPC_SYSDEV_MPIC_H */ diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 52fc836f464d..4aaf18e83c8c 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -312,7 +312,8 @@ static void sun4u_irq_enable(unsigned int virt_irq) } } -static void sun4u_set_affinity(unsigned int virt_irq, cpumask_t mask) +static void sun4u_set_affinity(unsigned int virt_irq, + const struct cpumask *mask) { sun4u_irq_enable(virt_irq); } @@ -362,7 +363,8 @@ static void sun4v_irq_enable(unsigned int virt_irq) ino, err); } -static void sun4v_set_affinity(unsigned int virt_irq, cpumask_t mask) +static void sun4v_set_affinity(unsigned int virt_irq, + const struct cpumask *mask) { unsigned int ino = virt_irq_table[virt_irq].dev_ino; unsigned long cpuid = irq_choose_cpu(virt_irq); @@ -429,7 +431,8 @@ static void sun4v_virq_enable(unsigned int virt_irq) dev_handle, dev_ino, err); } -static void sun4v_virt_set_affinity(unsigned int virt_irq, cpumask_t mask) +static void sun4v_virt_set_affinity(unsigned int virt_irq, + const struct cpumask *mask) { unsigned long cpuid, dev_handle, dev_ino; int err; @@ -788,7 +791,7 @@ void fixup_irqs(void) !(irq_desc[irq].status & IRQ_PER_CPU)) { if (irq_desc[irq].chip->set_affinity) irq_desc[irq].chip->set_affinity(irq, - irq_desc[irq].affinity); + &irq_desc[irq].affinity); } spin_unlock_irqrestore(&irq_desc[irq].lock, flags); } diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c index 0f616ae3246c..df2efb7fc14c 100644 --- a/arch/sparc64/kernel/of_device.c +++ b/arch/sparc64/kernel/of_device.c @@ -780,7 +780,7 @@ out: if (nid != -1) { cpumask_t numa_mask = node_to_cpumask(nid); - irq_set_affinity(irq, numa_mask); + irq_set_affinity(irq, &numa_mask); } return irq; diff --git a/arch/sparc64/kernel/pci_msi.c b/arch/sparc64/kernel/pci_msi.c index 2e680f34f727..0d0cd815e83e 100644 --- a/arch/sparc64/kernel/pci_msi.c +++ b/arch/sparc64/kernel/pci_msi.c @@ -288,7 +288,7 @@ static int bringup_one_msi_queue(struct pci_pbm_info *pbm, if (nid != -1) { cpumask_t numa_mask = node_to_cpumask(nid); - irq_set_affinity(irq, numa_mask); + irq_set_affinity(irq, &numa_mask); } err = request_irq(irq, sparc64_msiq_interrupt, 0, "MSIQ", diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 067d8de913f6..940f25851e1e 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -301,7 +301,7 @@ static void hpet_set_mode(enum clock_event_mode mode, struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); hpet_setup_msi_irq(hdev->irq); disable_irq(hdev->irq); - irq_set_affinity(hdev->irq, cpumask_of_cpu(hdev->cpu)); + irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); enable_irq(hdev->irq); } break; @@ -449,7 +449,7 @@ static int hpet_setup_irq(struct hpet_dev *dev) return -1; disable_irq(dev->irq); - irq_set_affinity(dev->irq, cpumask_of_cpu(dev->cpu)); + irq_set_affinity(dev->irq, cpumask_of(dev->cpu)); enable_irq(dev->irq); printk(KERN_DEBUG "hpet: %s irq %d for MSI\n", diff --git a/arch/x86/kernel/io_apic.c b/arch/x86/kernel/io_apic.c index 9043251210fb..1184210e6d0c 100644 --- a/arch/x86/kernel/io_apic.c +++ b/arch/x86/kernel/io_apic.c @@ -361,7 +361,8 @@ static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector) static int assign_irq_vector(int irq, cpumask_t mask); -static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) +static void set_ioapic_affinity_irq(unsigned int irq, + const struct cpumask *mask) { struct irq_cfg *cfg; unsigned long flags; @@ -369,15 +370,14 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) cpumask_t tmp; struct irq_desc *desc; - cpus_and(tmp, mask, cpu_online_map); - if (cpus_empty(tmp)) + if (!cpumask_intersects(mask, cpu_online_mask)) return; cfg = irq_cfg(irq); - if (assign_irq_vector(irq, mask)) + if (assign_irq_vector(irq, *mask)) return; - cpus_and(tmp, cfg->domain, mask); + cpumask_and(&tmp, &cfg->domain, mask); dest = cpu_mask_to_apicid(tmp); /* * Only the high 8 bits are valid. @@ -387,7 +387,7 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) desc = irq_to_desc(irq); spin_lock_irqsave(&ioapic_lock, flags); __target_IO_APIC_irq(irq, dest, cfg->vector); - desc->affinity = mask; + cpumask_copy(&desc->affinity, mask); spin_unlock_irqrestore(&ioapic_lock, flags); } #endif /* CONFIG_SMP */ @@ -2189,7 +2189,7 @@ static void ir_irq_migration(struct work_struct *work) continue; } - desc->chip->set_affinity(irq, desc->pending_mask); + desc->chip->set_affinity(irq, &desc->pending_mask); spin_unlock_irqrestore(&desc->lock, flags); } } @@ -2198,18 +2198,19 @@ static void ir_irq_migration(struct work_struct *work) /* * Migrates the IRQ destination in the process context. */ -static void set_ir_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) +static void set_ir_ioapic_affinity_irq(unsigned int irq, + const struct cpumask *mask) { struct irq_desc *desc = irq_to_desc(irq); if (desc->status & IRQ_LEVEL) { desc->status |= IRQ_MOVE_PENDING; - desc->pending_mask = mask; + cpumask_copy(&desc->pending_mask, mask); migrate_irq_remapped_level(irq); return; } - migrate_ioapic_irq(irq, mask); + migrate_ioapic_irq(irq, *mask); } #endif @@ -3027,7 +3028,7 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms } #ifdef CONFIG_SMP -static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) +static void set_msi_irq_affinity(unsigned int irq, const struct cpumask *mask) { struct irq_cfg *cfg; struct msi_msg msg; @@ -3035,15 +3036,14 @@ static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) cpumask_t tmp; struct irq_desc *desc; - cpus_and(tmp, mask, cpu_online_map); - if (cpus_empty(tmp)) + if (!cpumask_intersects(mask, cpu_online_mask)) return; - if (assign_irq_vector(irq, mask)) + if (assign_irq_vector(irq, *mask)) return; cfg = irq_cfg(irq); - cpus_and(tmp, cfg->domain, mask); + cpumask_and(&tmp, &cfg->domain, mask); dest = cpu_mask_to_apicid(tmp); read_msi_msg(irq, &msg); @@ -3055,7 +3055,7 @@ static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) write_msi_msg(irq, &msg); desc = irq_to_desc(irq); - desc->affinity = mask; + cpumask_copy(&desc->affinity, mask); } #ifdef CONFIG_INTR_REMAP @@ -3063,7 +3063,8 @@ static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) * Migrate the MSI irq to another cpumask. This migration is * done in the process context using interrupt-remapping hardware. */ -static void ir_set_msi_irq_affinity(unsigned int irq, cpumask_t mask) +static void ir_set_msi_irq_affinity(unsigned int irq, + const struct cpumask *mask) { struct irq_cfg *cfg; unsigned int dest; @@ -3071,18 +3072,17 @@ static void ir_set_msi_irq_affinity(unsigned int irq, cpumask_t mask) struct irte irte; struct irq_desc *desc; - cpus_and(tmp, mask, cpu_online_map); - if (cpus_empty(tmp)) + if (!cpumask_intersects(mask, cpu_online_mask)) return; if (get_irte(irq, &irte)) return; - if (assign_irq_vector(irq, mask)) + if (assign_irq_vector(irq, *mask)) return; cfg = irq_cfg(irq); - cpus_and(tmp, cfg->domain, mask); + cpumask_and(&tmp, &cfg->domain, mask); dest = cpu_mask_to_apicid(tmp); irte.vector = cfg->vector; @@ -3106,7 +3106,7 @@ static void ir_set_msi_irq_affinity(unsigned int irq, cpumask_t mask) } desc = irq_to_desc(irq); - desc->affinity = mask; + cpumask_copy(&desc->affinity, mask); } #endif #endif /* CONFIG_SMP */ @@ -3308,7 +3308,7 @@ void arch_teardown_msi_irq(unsigned int irq) #ifdef CONFIG_DMAR #ifdef CONFIG_SMP -static void dmar_msi_set_affinity(unsigned int irq, cpumask_t mask) +static void dmar_msi_set_affinity(unsigned int irq, const struct cpumask *mask) { struct irq_cfg *cfg; struct msi_msg msg; @@ -3316,15 +3316,14 @@ static void dmar_msi_set_affinity(unsigned int irq, cpumask_t mask) cpumask_t tmp; struct irq_desc *desc; - cpus_and(tmp, mask, cpu_online_map); - if (cpus_empty(tmp)) + if (!cpumask_intersects(mask, cpu_online_mask)) return; - if (assign_irq_vector(irq, mask)) + if (assign_irq_vector(irq, *mask)) return; cfg = irq_cfg(irq); - cpus_and(tmp, cfg->domain, mask); + cpumask_and(&tmp, &cfg->domain, mask); dest = cpu_mask_to_apicid(tmp); dmar_msi_read(irq, &msg); @@ -3336,7 +3335,7 @@ static void dmar_msi_set_affinity(unsigned int irq, cpumask_t mask) dmar_msi_write(irq, &msg); desc = irq_to_desc(irq); - desc->affinity = mask; + cpumask_copy(&desc->affinity, mask); } #endif /* CONFIG_SMP */ @@ -3369,7 +3368,7 @@ int arch_setup_dmar_msi(unsigned int irq) #ifdef CONFIG_HPET_TIMER #ifdef CONFIG_SMP -static void hpet_msi_set_affinity(unsigned int irq, cpumask_t mask) +static void hpet_msi_set_affinity(unsigned int irq, const struct cpumask *mask) { struct irq_cfg *cfg; struct irq_desc *desc; @@ -3377,15 +3376,14 @@ static void hpet_msi_set_affinity(unsigned int irq, cpumask_t mask) unsigned int dest; cpumask_t tmp; - cpus_and(tmp, mask, cpu_online_map); - if (cpus_empty(tmp)) + if (!cpumask_intersects(mask, cpu_online_mask)) return; - if (assign_irq_vector(irq, mask)) + if (assign_irq_vector(irq, *mask)) return; cfg = irq_cfg(irq); - cpus_and(tmp, cfg->domain, mask); + cpumask_and(&tmp, &cfg->domain, mask); dest = cpu_mask_to_apicid(tmp); hpet_msi_read(irq, &msg); @@ -3397,7 +3395,7 @@ static void hpet_msi_set_affinity(unsigned int irq, cpumask_t mask) hpet_msi_write(irq, &msg); desc = irq_to_desc(irq); - desc->affinity = mask; + cpumask_copy(&desc->affinity, mask); } #endif /* CONFIG_SMP */ @@ -3451,27 +3449,26 @@ static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector) write_ht_irq_msg(irq, &msg); } -static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask) +static void set_ht_irq_affinity(unsigned int irq, const struct cpumask *mask) { struct irq_cfg *cfg; unsigned int dest; cpumask_t tmp; struct irq_desc *desc; - cpus_and(tmp, mask, cpu_online_map); - if (cpus_empty(tmp)) + if (!cpumask_intersects(mask, cpu_online_mask)) return; - if (assign_irq_vector(irq, mask)) + if (assign_irq_vector(irq, *mask)) return; cfg = irq_cfg(irq); - cpus_and(tmp, cfg->domain, mask); + cpumask_and(&tmp, &cfg->domain, mask); dest = cpu_mask_to_apicid(tmp); target_ht_irq(irq, dest, cfg->vector); desc = irq_to_desc(irq); - desc->affinity = mask; + cpumask_copy(&desc->affinity, mask); } #endif @@ -3794,10 +3791,10 @@ void __init setup_ioapic_dest(void) #ifdef CONFIG_INTR_REMAP if (intr_remapping_enabled) - set_ir_ioapic_affinity_irq(irq, mask); + set_ir_ioapic_affinity_irq(irq, &mask); else #endif - set_ioapic_affinity_irq(irq, mask); + set_ioapic_affinity_irq(irq, &mask); } } diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c index a51382672de0..87870a49be4e 100644 --- a/arch/x86/kernel/irq_32.c +++ b/arch/x86/kernel/irq_32.c @@ -251,7 +251,7 @@ void fixup_irqs(cpumask_t map) mask = map; } if (desc->chip->set_affinity) - desc->chip->set_affinity(irq, mask); + desc->chip->set_affinity(irq, &mask); else if (desc->action && !(warned++)) printk("Cannot set affinity for irq %i\n", irq); } diff --git a/arch/x86/kernel/irq_64.c b/arch/x86/kernel/irq_64.c index 60eb84eb77a0..7d37f847544d 100644 --- a/arch/x86/kernel/irq_64.c +++ b/arch/x86/kernel/irq_64.c @@ -116,7 +116,7 @@ void fixup_irqs(cpumask_t map) desc->chip->mask(irq); if (desc->chip->set_affinity) - desc->chip->set_affinity(irq, mask); + desc->chip->set_affinity(irq, &mask); else if (!(warned++)) set_affinity = 0; diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c index 7beffcab2745..9dedbbd218c3 100644 --- a/drivers/parisc/iosapic.c +++ b/drivers/parisc/iosapic.c @@ -704,16 +704,17 @@ static unsigned int iosapic_startup_irq(unsigned int irq) } #ifdef CONFIG_SMP -static void iosapic_set_affinity_irq(unsigned int irq, cpumask_t dest) +static void iosapic_set_affinity_irq(unsigned int irq, + const struct cpumask *dest) { struct vector_info *vi = iosapic_get_vector(irq); u32 d0, d1, dummy_d0; unsigned long flags; - if (cpu_check_affinity(irq, &dest)) + if (cpu_check_affinity(irq, dest)) return; - vi->txn_addr = txn_affinity_addr(irq, first_cpu(dest)); + vi->txn_addr = txn_affinity_addr(irq, cpumask_first(dest)); spin_lock_irqsave(&iosapic_lock, flags); /* d1 contains the destination CPU, so only want to set that diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 1e3b934a4cf7..eba5ec5b020e 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -579,7 +579,7 @@ void rebind_evtchn_irq(int evtchn, int irq) spin_unlock(&irq_mapping_update_lock); /* new event channels are always bound to cpu 0 */ - irq_set_affinity(irq, cpumask_of_cpu(0)); + irq_set_affinity(irq, cpumask_of(0)); /* Unmask the event channel. */ enable_irq(irq); @@ -608,9 +608,9 @@ static void rebind_irq_to_cpu(unsigned irq, unsigned tcpu) } -static void set_affinity_irq(unsigned irq, cpumask_t dest) +static void set_affinity_irq(unsigned irq, const struct cpumask *dest) { - unsigned tcpu = first_cpu(dest); + unsigned tcpu = cpumask_first(dest); rebind_irq_to_cpu(irq, tcpu); } diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index f58a0cf8929a..48e63934fabe 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -109,13 +109,13 @@ extern void enable_irq(unsigned int irq); extern cpumask_t irq_default_affinity; -extern int irq_set_affinity(unsigned int irq, cpumask_t cpumask); +extern int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask); extern int irq_can_set_affinity(unsigned int irq); extern int irq_select_affinity(unsigned int irq); #else /* CONFIG_SMP */ -static inline int irq_set_affinity(unsigned int irq, cpumask_t cpumask) +static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m) { return -EINVAL; } diff --git a/include/linux/irq.h b/include/linux/irq.h index 3dddfa703ebd..ab70fd604d3a 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -113,7 +113,8 @@ struct irq_chip { void (*eoi)(unsigned int irq); void (*end)(unsigned int irq); - void (*set_affinity)(unsigned int irq, cpumask_t dest); + void (*set_affinity)(unsigned int irq, + const struct cpumask *dest); int (*retrigger)(unsigned int irq); int (*set_type)(unsigned int irq, unsigned int flow_type); int (*set_wake)(unsigned int irq, unsigned int on); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 10b5092e9bfe..58d8e31daa49 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -45,7 +45,7 @@ void dynamic_irq_init(unsigned int irq) desc->irq_count = 0; desc->irqs_unhandled = 0; #ifdef CONFIG_SMP - cpus_setall(desc->affinity); + cpumask_setall(&desc->affinity); #endif spin_unlock_irqrestore(&desc->lock, flags); } diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 801addda3c43..10ad2f87ed9a 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -79,7 +79,7 @@ int irq_can_set_affinity(unsigned int irq) * @cpumask: cpumask * */ -int irq_set_affinity(unsigned int irq, cpumask_t cpumask) +int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask) { struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; @@ -91,14 +91,14 @@ int irq_set_affinity(unsigned int irq, cpumask_t cpumask) #ifdef CONFIG_GENERIC_PENDING_IRQ if (desc->status & IRQ_MOVE_PCNTXT || desc->status & IRQ_DISABLED) { - desc->affinity = cpumask; + cpumask_copy(&desc->affinity, cpumask); desc->chip->set_affinity(irq, cpumask); } else { desc->status |= IRQ_MOVE_PENDING; - desc->pending_mask = cpumask; + cpumask_copy(&desc->pending_mask, cpumask); } #else - desc->affinity = cpumask; + cpumask_copy(&desc->affinity, cpumask); desc->chip->set_affinity(irq, cpumask); #endif desc->status |= IRQ_AFFINITY_SET; @@ -112,26 +112,24 @@ int irq_set_affinity(unsigned int irq, cpumask_t cpumask) */ int do_irq_select_affinity(unsigned int irq, struct irq_desc *desc) { - cpumask_t mask; - if (!irq_can_set_affinity(irq)) return 0; - cpus_and(mask, cpu_online_map, irq_default_affinity); - /* * Preserve an userspace affinity setup, but make sure that * one of the targets is online. */ if (desc->status & (IRQ_AFFINITY_SET | IRQ_NO_BALANCING)) { - if (cpus_intersects(desc->affinity, cpu_online_map)) - mask = desc->affinity; + if (cpumask_any_and(&desc->affinity, cpu_online_mask) + < nr_cpu_ids) + goto set_affinity; else desc->status &= ~IRQ_AFFINITY_SET; } - desc->affinity = mask; - desc->chip->set_affinity(irq, mask); + cpumask_and(&desc->affinity, cpu_online_mask, &irq_default_affinity); +set_affinity: + desc->chip->set_affinity(irq, &desc->affinity); return 0; } diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c index 9db681d95814..bd72329e630c 100644 --- a/kernel/irq/migration.c +++ b/kernel/irq/migration.c @@ -4,7 +4,6 @@ void move_masked_irq(int irq) { struct irq_desc *desc = irq_to_desc(irq); - cpumask_t tmp; if (likely(!(desc->status & IRQ_MOVE_PENDING))) return; @@ -19,7 +18,7 @@ void move_masked_irq(int irq) desc->status &= ~IRQ_MOVE_PENDING; - if (unlikely(cpus_empty(desc->pending_mask))) + if (unlikely(cpumask_empty(&desc->pending_mask))) return; if (!desc->chip->set_affinity) @@ -27,8 +26,6 @@ void move_masked_irq(int irq) assert_spin_locked(&desc->lock); - cpus_and(tmp, desc->pending_mask, cpu_online_map); - /* * If there was a valid mask to work with, please * do the disable, re-program, enable sequence. @@ -41,10 +38,13 @@ void move_masked_irq(int irq) * For correct operation this depends on the caller * masking the irqs. */ - if (likely(!cpus_empty(tmp))) { - desc->chip->set_affinity(irq,tmp); + if (likely(cpumask_any_and(&desc->pending_mask, cpu_online_mask) + < nr_cpu_ids)) { + cpumask_and(&desc->affinity, + &desc->pending_mask, cpu_online_mask); + desc->chip->set_affinity(irq, &desc->affinity); } - cpus_clear(desc->pending_mask); + cpumask_clear(&desc->pending_mask); } void move_native_irq(int irq) diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index f293349d49d0..8e91c9762520 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -40,33 +40,42 @@ static ssize_t irq_affinity_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { unsigned int irq = (int)(long)PDE(file->f_path.dentry->d_inode)->data; - cpumask_t new_value; + cpumask_var_t new_value; int err; if (!irq_to_desc(irq)->chip->set_affinity || no_irq_affinity || irq_balancing_disabled(irq)) return -EIO; - err = cpumask_parse_user(buffer, count, &new_value); + if (!alloc_cpumask_var(&new_value, GFP_KERNEL)) + return -ENOMEM; + + err = cpumask_parse_user(buffer, count, new_value); if (err) - return err; + goto free_cpumask; - if (!is_affinity_mask_valid(new_value)) - return -EINVAL; + if (!is_affinity_mask_valid(*new_value)) { + err = -EINVAL; + goto free_cpumask; + } /* * Do not allow disabling IRQs completely - it's a too easy * way to make the system unusable accidentally :-) At least * one online CPU still has to be targeted. */ - if (!cpus_intersects(new_value, cpu_online_map)) + if (!cpumask_intersects(new_value, cpu_online_mask)) { /* Special case for empty set - allow the architecture code to set default SMP affinity. */ - return irq_select_affinity_usr(irq) ? -EINVAL : count; - - irq_set_affinity(irq, new_value); + err = irq_select_affinity_usr(irq) ? -EINVAL : count; + } else { + irq_set_affinity(irq, new_value); + err = count; + } - return count; +free_cpumask: + free_cpumask_var(new_value); + return err; } static int irq_affinity_proc_open(struct inode *inode, struct file *file) diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index df12434b43ca..ab65d217583f 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -136,7 +136,7 @@ void tick_setup_periodic(struct clock_event_device *dev, int broadcast) */ static void tick_setup_device(struct tick_device *td, struct clock_event_device *newdev, int cpu, - const cpumask_t *cpumask) + const struct cpumask *cpumask) { ktime_t next_event; void (*handler)(struct clock_event_device *) = NULL; @@ -171,8 +171,8 @@ static void tick_setup_device(struct tick_device *td, * When the device is not per cpu, pin the interrupt to the * current cpu: */ - if (!cpus_equal(newdev->cpumask, *cpumask)) - irq_set_affinity(newdev->irq, *cpumask); + if (!cpumask_equal(&newdev->cpumask, cpumask)) + irq_set_affinity(newdev->irq, cpumask); /* * When global broadcasting is active, check if the current -- cgit v1.2.3 From 320ab2b0b1e08e3805a3e1084a2f0eb1938d5d67 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 13 Dec 2008 21:20:26 +1030 Subject: cpumask: convert struct clock_event_device to cpumask pointers. Impact: change calling convention of existing clock_event APIs struct clock_event_timer's cpumask field gets changed to take pointer, as does the ->broadcast function. Another single-patch change. For safety, we BUG_ON() in clockevents_register_device() if it's not set. Signed-off-by: Rusty Russell Cc: Ingo Molnar --- arch/arm/mach-at91/at91rm9200_time.c | 3 +-- arch/arm/mach-at91/at91sam926x_time.c | 2 +- arch/arm/mach-davinci/time.c | 2 +- arch/arm/mach-imx/time.c | 2 +- arch/arm/mach-ixp4xx/common.c | 2 +- arch/arm/mach-msm/timer.c | 2 +- arch/arm/mach-ns9xxx/time-ns9360.c | 2 +- arch/arm/mach-omap1/time.c | 2 +- arch/arm/mach-omap1/timer32k.c | 2 +- arch/arm/mach-omap2/timer-gp.c | 2 +- arch/arm/mach-pxa/time.c | 2 +- arch/arm/mach-realview/core.c | 2 +- arch/arm/mach-realview/localtimer.c | 4 ++-- arch/arm/mach-sa1100/time.c | 2 +- arch/arm/mach-versatile/core.c | 2 +- arch/arm/plat-mxc/time.c | 2 +- arch/arm/plat-orion/time.c | 2 +- arch/avr32/kernel/time.c | 2 +- arch/blackfin/kernel/time-ts.c | 2 +- arch/m68knommu/platform/coldfire/pit.c | 2 +- arch/mips/jazz/irq.c | 2 +- arch/mips/kernel/cevt-bcm1480.c | 2 +- arch/mips/kernel/cevt-ds1287.c | 2 +- arch/mips/kernel/cevt-gt641xx.c | 2 +- arch/mips/kernel/cevt-r4k.c | 2 +- arch/mips/kernel/cevt-sb1250.c | 2 +- arch/mips/kernel/cevt-smtc.c | 2 +- arch/mips/kernel/cevt-txx9.c | 2 +- arch/mips/kernel/i8253.c | 2 +- arch/mips/nxp/pnx8550/common/time.c | 1 + arch/mips/sgi-ip27/ip27-timer.c | 2 +- arch/mips/sni/time.c | 2 +- arch/powerpc/kernel/time.c | 2 +- arch/s390/kernel/time.c | 2 +- arch/sh/include/asm/smp.h | 2 +- arch/sh/kernel/smp.c | 4 ++-- arch/sh/kernel/timers/timer-broadcast.c | 2 +- arch/sh/kernel/timers/timer-tmu.c | 2 +- arch/sparc64/kernel/time.c | 2 +- arch/um/kernel/time.c | 2 +- arch/x86/kernel/apic.c | 8 ++++---- arch/x86/kernel/hpet.c | 4 ++-- arch/x86/kernel/i8253.c | 2 +- arch/x86/kernel/mfgpt_32.c | 2 +- arch/x86/kernel/vmiclock_32.c | 2 +- arch/x86/lguest/boot.c | 2 +- arch/x86/xen/time.c | 2 +- drivers/clocksource/tcb_clksrc.c | 2 +- include/linux/clockchips.h | 4 ++-- kernel/time/clockevents.c | 2 ++ kernel/time/tick-broadcast.c | 2 +- kernel/time/tick-common.c | 8 ++++---- 52 files changed, 63 insertions(+), 61 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-at91/at91rm9200_time.c b/arch/arm/mach-at91/at91rm9200_time.c index a72e798a2a40..72f51d39202c 100644 --- a/arch/arm/mach-at91/at91rm9200_time.c +++ b/arch/arm/mach-at91/at91rm9200_time.c @@ -169,7 +169,6 @@ static struct clock_event_device clkevt = { .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .shift = 32, .rating = 150, - .cpumask = CPU_MASK_CPU0, .set_next_event = clkevt32k_next_event, .set_mode = clkevt32k_mode, }; @@ -197,7 +196,7 @@ void __init at91rm9200_timer_init(void) clkevt.mult = div_sc(AT91_SLOW_CLOCK, NSEC_PER_SEC, clkevt.shift); clkevt.max_delta_ns = clockevent_delta2ns(AT91_ST_ALMV, &clkevt); clkevt.min_delta_ns = clockevent_delta2ns(2, &clkevt) + 1; - clkevt.cpumask = cpumask_of_cpu(0); + clkevt.cpumask = cpumask_of(0); clockevents_register_device(&clkevt); /* register clocksource */ diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index 122fd77ed580..b63e1d5f1bad 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -91,7 +91,6 @@ static struct clock_event_device pit_clkevt = { .features = CLOCK_EVT_FEAT_PERIODIC, .shift = 32, .rating = 100, - .cpumask = CPU_MASK_CPU0, .set_mode = pit_clkevt_mode, }; @@ -173,6 +172,7 @@ static void __init at91sam926x_pit_init(void) /* Set up and register clockevents */ pit_clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, pit_clkevt.shift); + pit_clkevt.cpumask = cpumask_of(0); clockevents_register_device(&pit_clkevt); } diff --git a/arch/arm/mach-davinci/time.c b/arch/arm/mach-davinci/time.c index 3b9a296b5c4b..f8bcd29d17a6 100644 --- a/arch/arm/mach-davinci/time.c +++ b/arch/arm/mach-davinci/time.c @@ -322,7 +322,7 @@ static void __init davinci_timer_init(void) clockevent_davinci.min_delta_ns = clockevent_delta2ns(1, &clockevent_davinci); - clockevent_davinci.cpumask = cpumask_of_cpu(0); + clockevent_davinci.cpumask = cpumask_of(0); clockevents_register_device(&clockevent_davinci); } diff --git a/arch/arm/mach-imx/time.c b/arch/arm/mach-imx/time.c index a11765f5f23b..aff0ebcfa847 100644 --- a/arch/arm/mach-imx/time.c +++ b/arch/arm/mach-imx/time.c @@ -184,7 +184,7 @@ static int __init imx_clockevent_init(unsigned long rate) clockevent_imx.min_delta_ns = clockevent_delta2ns(0xf, &clockevent_imx); - clockevent_imx.cpumask = cpumask_of_cpu(0); + clockevent_imx.cpumask = cpumask_of(0); clockevents_register_device(&clockevent_imx); diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index 7766f469456b..f4656d2ac8a8 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -487,7 +487,7 @@ static int __init ixp4xx_clockevent_init(void) clockevent_delta2ns(0xfffffffe, &clockevent_ixp4xx); clockevent_ixp4xx.min_delta_ns = clockevent_delta2ns(0xf, &clockevent_ixp4xx); - clockevent_ixp4xx.cpumask = cpumask_of_cpu(0); + clockevent_ixp4xx.cpumask = cpumask_of(0); clockevents_register_device(&clockevent_ixp4xx); return 0; diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 345a14cb73c3..444d9c0f5ca6 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -182,7 +182,7 @@ static void __init msm_timer_init(void) clockevent_delta2ns(0xf0000000 >> clock->shift, ce); /* 4 gets rounded down to 3 */ ce->min_delta_ns = clockevent_delta2ns(4, ce); - ce->cpumask = cpumask_of_cpu(0); + ce->cpumask = cpumask_of(0); cs->mult = clocksource_hz2mult(clock->freq, cs->shift); res = clocksource_register(cs); diff --git a/arch/arm/mach-ns9xxx/time-ns9360.c b/arch/arm/mach-ns9xxx/time-ns9360.c index a63424d083d9..41df69721769 100644 --- a/arch/arm/mach-ns9xxx/time-ns9360.c +++ b/arch/arm/mach-ns9xxx/time-ns9360.c @@ -173,7 +173,7 @@ static void __init ns9360_timer_init(void) ns9360_clockevent_device.min_delta_ns = clockevent_delta2ns(1, &ns9360_clockevent_device); - ns9360_clockevent_device.cpumask = cpumask_of_cpu(0); + ns9360_clockevent_device.cpumask = cpumask_of(0); clockevents_register_device(&ns9360_clockevent_device); setup_irq(IRQ_NS9360_TIMER0 + TIMER_CLOCKEVENT, diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index 2cf7e32bd293..495a32c287b4 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c @@ -173,7 +173,7 @@ static __init void omap_init_mpu_timer(unsigned long rate) clockevent_mpu_timer1.min_delta_ns = clockevent_delta2ns(1, &clockevent_mpu_timer1); - clockevent_mpu_timer1.cpumask = cpumask_of_cpu(0); + clockevent_mpu_timer1.cpumask = cpumask_of(0); clockevents_register_device(&clockevent_mpu_timer1); } diff --git a/arch/arm/mach-omap1/timer32k.c b/arch/arm/mach-omap1/timer32k.c index 705367ece174..fd3f7396e162 100644 --- a/arch/arm/mach-omap1/timer32k.c +++ b/arch/arm/mach-omap1/timer32k.c @@ -187,7 +187,7 @@ static __init void omap_init_32k_timer(void) clockevent_32k_timer.min_delta_ns = clockevent_delta2ns(1, &clockevent_32k_timer); - clockevent_32k_timer.cpumask = cpumask_of_cpu(0); + clockevent_32k_timer.cpumask = cpumask_of(0); clockevents_register_device(&clockevent_32k_timer); } diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c index 589393bedade..ae6036300f60 100644 --- a/arch/arm/mach-omap2/timer-gp.c +++ b/arch/arm/mach-omap2/timer-gp.c @@ -120,7 +120,7 @@ static void __init omap2_gp_clockevent_init(void) clockevent_gpt.min_delta_ns = clockevent_delta2ns(1, &clockevent_gpt); - clockevent_gpt.cpumask = cpumask_of_cpu(0); + clockevent_gpt.cpumask = cpumask_of(0); clockevents_register_device(&clockevent_gpt); } diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c index f8a9a62959e5..bf3c9a4aad50 100644 --- a/arch/arm/mach-pxa/time.c +++ b/arch/arm/mach-pxa/time.c @@ -122,7 +122,6 @@ static struct clock_event_device ckevt_pxa_osmr0 = { .features = CLOCK_EVT_FEAT_ONESHOT, .shift = 32, .rating = 200, - .cpumask = CPU_MASK_CPU0, .set_next_event = pxa_osmr0_set_next_event, .set_mode = pxa_osmr0_set_mode, }; @@ -170,6 +169,7 @@ static void __init pxa_timer_init(void) clockevent_delta2ns(0x7fffffff, &ckevt_pxa_osmr0); ckevt_pxa_osmr0.min_delta_ns = clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_pxa_osmr0) + 1; + ckevt_pxa_osmr0.cpumask = cpumask_of(0); cksrc_pxa_oscr0.mult = clocksource_hz2mult(clock_tick_rate, cksrc_pxa_oscr0.shift); diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 2f04d54711e7..b07cb9b7adb1 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -511,7 +511,7 @@ static struct clock_event_device timer0_clockevent = { .set_mode = timer_set_mode, .set_next_event = timer_set_next_event, .rating = 300, - .cpumask = CPU_MASK_ALL, + .cpumask = cpu_all_mask, }; static void __init realview_clockevents_init(unsigned int timer_irq) diff --git a/arch/arm/mach-realview/localtimer.c b/arch/arm/mach-realview/localtimer.c index 44d178cd5733..504961ef343c 100644 --- a/arch/arm/mach-realview/localtimer.c +++ b/arch/arm/mach-realview/localtimer.c @@ -161,7 +161,7 @@ void __cpuinit local_timer_setup(unsigned int cpu) clk->set_mode = local_timer_set_mode; clk->set_next_event = local_timer_set_next_event; clk->irq = IRQ_LOCALTIMER; - clk->cpumask = cpumask_of_cpu(cpu); + clk->cpumask = cpumask_of(cpu); clk->shift = 20; clk->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, clk->shift); clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); @@ -199,7 +199,7 @@ void __cpuinit local_timer_setup(unsigned int cpu) clk->rating = 200; clk->set_mode = dummy_timer_set_mode; clk->broadcast = smp_timer_broadcast; - clk->cpumask = cpumask_of_cpu(cpu); + clk->cpumask = cpumask_of(cpu); clockevents_register_device(clk); } diff --git a/arch/arm/mach-sa1100/time.c b/arch/arm/mach-sa1100/time.c index 24c0a4bae850..1cac4ac0b4b8 100644 --- a/arch/arm/mach-sa1100/time.c +++ b/arch/arm/mach-sa1100/time.c @@ -73,7 +73,6 @@ static struct clock_event_device ckevt_sa1100_osmr0 = { .features = CLOCK_EVT_FEAT_ONESHOT, .shift = 32, .rating = 200, - .cpumask = CPU_MASK_CPU0, .set_next_event = sa1100_osmr0_set_next_event, .set_mode = sa1100_osmr0_set_mode, }; @@ -110,6 +109,7 @@ static void __init sa1100_timer_init(void) clockevent_delta2ns(0x7fffffff, &ckevt_sa1100_osmr0); ckevt_sa1100_osmr0.min_delta_ns = clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_sa1100_osmr0) + 1; + ckevt_sa1100_osmr0.cpumask = cpumask_of(0); cksrc_sa1100_oscr.mult = clocksource_hz2mult(CLOCK_TICK_RATE, cksrc_sa1100_oscr.shift); diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index 565e0ba0d67e..a3f1933434e2 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -965,7 +965,7 @@ static void __init versatile_timer_init(void) timer0_clockevent.min_delta_ns = clockevent_delta2ns(0xf, &timer0_clockevent); - timer0_clockevent.cpumask = cpumask_of_cpu(0); + timer0_clockevent.cpumask = cpumask_of(0); clockevents_register_device(&timer0_clockevent); } diff --git a/arch/arm/plat-mxc/time.c b/arch/arm/plat-mxc/time.c index fd28f5194f71..758a1293bcfa 100644 --- a/arch/arm/plat-mxc/time.c +++ b/arch/arm/plat-mxc/time.c @@ -190,7 +190,7 @@ static int __init mxc_clockevent_init(void) clockevent_mxc.min_delta_ns = clockevent_delta2ns(0xff, &clockevent_mxc); - clockevent_mxc.cpumask = cpumask_of_cpu(0); + clockevent_mxc.cpumask = cpumask_of(0); clockevents_register_device(&clockevent_mxc); diff --git a/arch/arm/plat-orion/time.c b/arch/arm/plat-orion/time.c index 544d6b327f3a..6fa2923e6dca 100644 --- a/arch/arm/plat-orion/time.c +++ b/arch/arm/plat-orion/time.c @@ -149,7 +149,6 @@ static struct clock_event_device orion_clkevt = { .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, .shift = 32, .rating = 300, - .cpumask = CPU_MASK_CPU0, .set_next_event = orion_clkevt_next_event, .set_mode = orion_clkevt_mode, }; @@ -199,5 +198,6 @@ void __init orion_time_init(unsigned int irq, unsigned int tclk) orion_clkevt.mult = div_sc(tclk, NSEC_PER_SEC, orion_clkevt.shift); orion_clkevt.max_delta_ns = clockevent_delta2ns(0xfffffffe, &orion_clkevt); orion_clkevt.min_delta_ns = clockevent_delta2ns(1, &orion_clkevt); + orion_clkevt.cpumask = cpumask_of(0); clockevents_register_device(&orion_clkevt); } diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c index 283481d74a5b..0ff46bf873b0 100644 --- a/arch/avr32/kernel/time.c +++ b/arch/avr32/kernel/time.c @@ -106,7 +106,6 @@ static struct clock_event_device comparator = { .features = CLOCK_EVT_FEAT_ONESHOT, .shift = 16, .rating = 50, - .cpumask = CPU_MASK_CPU0, .set_next_event = comparator_next_event, .set_mode = comparator_mode, }; @@ -134,6 +133,7 @@ void __init time_init(void) comparator.mult = div_sc(counter_hz, NSEC_PER_SEC, comparator.shift); comparator.max_delta_ns = clockevent_delta2ns((u32)~0, &comparator); comparator.min_delta_ns = clockevent_delta2ns(50, &comparator) + 1; + comparator.cpumask = cpumask_of(0); sysreg_write(COMPARE, 0); timer_irqaction.dev_id = &comparator; diff --git a/arch/blackfin/kernel/time-ts.c b/arch/blackfin/kernel/time-ts.c index e887efc86c29..0ed2badfd746 100644 --- a/arch/blackfin/kernel/time-ts.c +++ b/arch/blackfin/kernel/time-ts.c @@ -162,7 +162,6 @@ static struct clock_event_device clockevent_bfin = { .name = "bfin_core_timer", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .shift = 32, - .cpumask = CPU_MASK_CPU0, .set_next_event = bfin_timer_set_next_event, .set_mode = bfin_timer_set_mode, }; @@ -193,6 +192,7 @@ static int __init bfin_clockevent_init(void) clockevent_bfin.mult = div_sc(timer_clk, NSEC_PER_SEC, clockevent_bfin.shift); clockevent_bfin.max_delta_ns = clockevent_delta2ns(-1, &clockevent_bfin); clockevent_bfin.min_delta_ns = clockevent_delta2ns(100, &clockevent_bfin); + clockevent_bfin.cpumask = cpumask_of(0); clockevents_register_device(&clockevent_bfin); return 0; diff --git a/arch/m68knommu/platform/coldfire/pit.c b/arch/m68knommu/platform/coldfire/pit.c index c5b916700b22..2a12e7fa9748 100644 --- a/arch/m68knommu/platform/coldfire/pit.c +++ b/arch/m68knommu/platform/coldfire/pit.c @@ -156,7 +156,7 @@ void hw_timer_init(void) { u32 imr; - cf_pit_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); + cf_pit_clockevent.cpumask = cpumask_of(smp_processor_id()); cf_pit_clockevent.mult = div_sc(FREQ, NSEC_PER_SEC, 32); cf_pit_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFF, &cf_pit_clockevent); diff --git a/arch/mips/jazz/irq.c b/arch/mips/jazz/irq.c index d7f8a782aae4..03965cb1b252 100644 --- a/arch/mips/jazz/irq.c +++ b/arch/mips/jazz/irq.c @@ -146,7 +146,7 @@ void __init plat_time_init(void) BUG_ON(HZ != 100); - cd->cpumask = cpumask_of_cpu(cpu); + cd->cpumask = cpumask_of(cpu); clockevents_register_device(cd); action->dev_id = cd; setup_irq(JAZZ_TIMER_IRQ, action); diff --git a/arch/mips/kernel/cevt-bcm1480.c b/arch/mips/kernel/cevt-bcm1480.c index d7e21bc8cd21..b820661678b0 100644 --- a/arch/mips/kernel/cevt-bcm1480.c +++ b/arch/mips/kernel/cevt-bcm1480.c @@ -126,7 +126,7 @@ void __cpuinit sb1480_clockevent_init(void) cd->min_delta_ns = clockevent_delta2ns(2, cd); cd->rating = 200; cd->irq = irq; - cd->cpumask = cpumask_of_cpu(cpu); + cd->cpumask = cpumask_of(cpu); cd->set_next_event = sibyte_next_event; cd->set_mode = sibyte_set_mode; clockevents_register_device(cd); diff --git a/arch/mips/kernel/cevt-ds1287.c b/arch/mips/kernel/cevt-ds1287.c index df4acb68bfb5..1ada45ea0700 100644 --- a/arch/mips/kernel/cevt-ds1287.c +++ b/arch/mips/kernel/cevt-ds1287.c @@ -88,7 +88,6 @@ static void ds1287_event_handler(struct clock_event_device *dev) static struct clock_event_device ds1287_clockevent = { .name = "ds1287", .features = CLOCK_EVT_FEAT_PERIODIC, - .cpumask = CPU_MASK_CPU0, .set_next_event = ds1287_set_next_event, .set_mode = ds1287_set_mode, .event_handler = ds1287_event_handler, @@ -122,6 +121,7 @@ int __init ds1287_clockevent_init(int irq) clockevent_set_clock(cd, 32768); cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); cd->min_delta_ns = clockevent_delta2ns(0x300, cd); + cd->cpumask = cpumask_of(0); clockevents_register_device(&ds1287_clockevent); diff --git a/arch/mips/kernel/cevt-gt641xx.c b/arch/mips/kernel/cevt-gt641xx.c index 6e2f58520afb..e9b787feedcb 100644 --- a/arch/mips/kernel/cevt-gt641xx.c +++ b/arch/mips/kernel/cevt-gt641xx.c @@ -96,7 +96,6 @@ static void gt641xx_timer0_event_handler(struct clock_event_device *dev) static struct clock_event_device gt641xx_timer0_clockevent = { .name = "gt641xx-timer0", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .cpumask = CPU_MASK_CPU0, .irq = GT641XX_TIMER0_IRQ, .set_next_event = gt641xx_timer0_set_next_event, .set_mode = gt641xx_timer0_set_mode, @@ -132,6 +131,7 @@ static int __init gt641xx_timer0_clockevent_init(void) clockevent_set_clock(cd, gt641xx_base_clock); cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); cd->min_delta_ns = clockevent_delta2ns(0x300, cd); + cd->cpumask = cpumask_of(0); clockevents_register_device(>641xx_timer0_clockevent); diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c index 4a4c59f2737a..e1ec83b68031 100644 --- a/arch/mips/kernel/cevt-r4k.c +++ b/arch/mips/kernel/cevt-r4k.c @@ -195,7 +195,7 @@ int __cpuinit mips_clockevent_init(void) cd->rating = 300; cd->irq = irq; - cd->cpumask = cpumask_of_cpu(cpu); + cd->cpumask = cpumask_of(cpu); cd->set_next_event = mips_next_event; cd->set_mode = mips_set_clock_mode; cd->event_handler = mips_event_handler; diff --git a/arch/mips/kernel/cevt-sb1250.c b/arch/mips/kernel/cevt-sb1250.c index 0f188cd46e03..a2eebaafda52 100644 --- a/arch/mips/kernel/cevt-sb1250.c +++ b/arch/mips/kernel/cevt-sb1250.c @@ -125,7 +125,7 @@ void __cpuinit sb1250_clockevent_init(void) cd->min_delta_ns = clockevent_delta2ns(2, cd); cd->rating = 200; cd->irq = irq; - cd->cpumask = cpumask_of_cpu(cpu); + cd->cpumask = cpumask_of(cpu); cd->set_next_event = sibyte_next_event; cd->set_mode = sibyte_set_mode; clockevents_register_device(cd); diff --git a/arch/mips/kernel/cevt-smtc.c b/arch/mips/kernel/cevt-smtc.c index 5162fe4b5952..6d45e24db5bf 100644 --- a/arch/mips/kernel/cevt-smtc.c +++ b/arch/mips/kernel/cevt-smtc.c @@ -292,7 +292,7 @@ int __cpuinit mips_clockevent_init(void) cd->rating = 300; cd->irq = irq; - cd->cpumask = cpumask_of_cpu(cpu); + cd->cpumask = cpumask_of(cpu); cd->set_next_event = mips_next_event; cd->set_mode = mips_set_clock_mode; cd->event_handler = mips_event_handler; diff --git a/arch/mips/kernel/cevt-txx9.c b/arch/mips/kernel/cevt-txx9.c index b5fc4eb412d2..eccf7d6096bd 100644 --- a/arch/mips/kernel/cevt-txx9.c +++ b/arch/mips/kernel/cevt-txx9.c @@ -112,7 +112,6 @@ static struct clock_event_device txx9tmr_clock_event_device = { .name = "TXx9", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .rating = 200, - .cpumask = CPU_MASK_CPU0, .set_mode = txx9tmr_set_mode, .set_next_event = txx9tmr_set_next_event, }; @@ -150,6 +149,7 @@ void __init txx9_clockevent_init(unsigned long baseaddr, int irq, clockevent_delta2ns(0xffffffff >> (32 - TXX9_TIMER_BITS), cd); cd->min_delta_ns = clockevent_delta2ns(0xf, cd); cd->irq = irq; + cd->cpumask = cpumask_of(0), clockevents_register_device(cd); setup_irq(irq, &txx9tmr_irq); printk(KERN_INFO "TXx9: clockevent device at 0x%lx, irq %d\n", diff --git a/arch/mips/kernel/i8253.c b/arch/mips/kernel/i8253.c index b6ac55162b9a..f4d187825f96 100644 --- a/arch/mips/kernel/i8253.c +++ b/arch/mips/kernel/i8253.c @@ -115,7 +115,7 @@ void __init setup_pit_timer(void) * Start pit with the boot cpu mask and make it global after the * IO_APIC has been initialized. */ - cd->cpumask = cpumask_of_cpu(cpu); + cd->cpumask = cpumask_of(cpu); clockevent_set_clock(cd, CLOCK_TICK_RATE); cd->max_delta_ns = clockevent_delta2ns(0x7FFF, cd); cd->min_delta_ns = clockevent_delta2ns(0xF, cd); diff --git a/arch/mips/nxp/pnx8550/common/time.c b/arch/mips/nxp/pnx8550/common/time.c index 62f495b57f93..cf293b279098 100644 --- a/arch/mips/nxp/pnx8550/common/time.c +++ b/arch/mips/nxp/pnx8550/common/time.c @@ -102,6 +102,7 @@ __init void plat_time_init(void) unsigned int p; unsigned int pow2p; + pnx8xxx_clockevent.cpumask = cpu_none_mask; clockevents_register_device(&pnx8xxx_clockevent); clocksource_register(&pnx_clocksource); diff --git a/arch/mips/sgi-ip27/ip27-timer.c b/arch/mips/sgi-ip27/ip27-timer.c index 1327c2746fb7..f024057a35f8 100644 --- a/arch/mips/sgi-ip27/ip27-timer.c +++ b/arch/mips/sgi-ip27/ip27-timer.c @@ -134,7 +134,7 @@ void __cpuinit hub_rt_clock_event_init(void) cd->min_delta_ns = clockevent_delta2ns(0x300, cd); cd->rating = 200; cd->irq = irq; - cd->cpumask = cpumask_of_cpu(cpu); + cd->cpumask = cpumask_of(cpu); cd->set_next_event = rt_next_event; cd->set_mode = rt_set_mode; clockevents_register_device(cd); diff --git a/arch/mips/sni/time.c b/arch/mips/sni/time.c index 796e3ce28720..69f5f88711cc 100644 --- a/arch/mips/sni/time.c +++ b/arch/mips/sni/time.c @@ -80,7 +80,7 @@ static void __init sni_a20r_timer_setup(void) struct irqaction *action = &a20r_irqaction; unsigned int cpu = smp_processor_id(); - cd->cpumask = cpumask_of_cpu(cpu); + cd->cpumask = cpumask_of(cpu); clockevents_register_device(cd); action->dev_id = cd; setup_irq(SNI_A20R_IRQ_TIMER, &a20r_irqaction); diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index e2ee66b5831d..6f39d35d6f55 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -869,7 +869,7 @@ static void register_decrementer_clockevent(int cpu) struct clock_event_device *dec = &per_cpu(decrementers, cpu).event; *dec = decrementer_clockevent; - dec->cpumask = cpumask_of_cpu(cpu); + dec->cpumask = cpumask_of(cpu); printk(KERN_DEBUG "clockevent: %s mult[%lx] shift[%d] cpu[%d]\n", dec->name, dec->mult, dec->shift, cpu); diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index eccefbbff887..f5bd141c8443 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -154,7 +154,7 @@ void init_cpu_timer(void) cd->min_delta_ns = 1; cd->max_delta_ns = LONG_MAX; cd->rating = 400; - cd->cpumask = cpumask_of_cpu(cpu); + cd->cpumask = cpumask_of(cpu); cd->set_next_event = s390_next_event; cd->set_mode = s390_set_mode; diff --git a/arch/sh/include/asm/smp.h b/arch/sh/include/asm/smp.h index 85b660c17eb0..c24e9c6a1736 100644 --- a/arch/sh/include/asm/smp.h +++ b/arch/sh/include/asm/smp.h @@ -31,7 +31,7 @@ enum { }; void smp_message_recv(unsigned int msg); -void smp_timer_broadcast(cpumask_t mask); +void smp_timer_broadcast(const struct cpumask *mask); void local_timer_interrupt(void); void local_timer_setup(unsigned int cpu); diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c index 593937d0c495..8f4027412614 100644 --- a/arch/sh/kernel/smp.c +++ b/arch/sh/kernel/smp.c @@ -184,11 +184,11 @@ void arch_send_call_function_single_ipi(int cpu) plat_send_ipi(cpu, SMP_MSG_FUNCTION_SINGLE); } -void smp_timer_broadcast(cpumask_t mask) +void smp_timer_broadcast(const struct cpumask *mask) { int cpu; - for_each_cpu_mask(cpu, mask) + for_each_cpu(cpu, mask) plat_send_ipi(cpu, SMP_MSG_TIMER); } diff --git a/arch/sh/kernel/timers/timer-broadcast.c b/arch/sh/kernel/timers/timer-broadcast.c index c2317635230f..96e8eaea1e62 100644 --- a/arch/sh/kernel/timers/timer-broadcast.c +++ b/arch/sh/kernel/timers/timer-broadcast.c @@ -51,7 +51,7 @@ void __cpuinit local_timer_setup(unsigned int cpu) clk->mult = 1; clk->set_mode = dummy_timer_set_mode; clk->broadcast = smp_timer_broadcast; - clk->cpumask = cpumask_of_cpu(cpu); + clk->cpumask = cpumask_of(cpu); clockevents_register_device(clk); } diff --git a/arch/sh/kernel/timers/timer-tmu.c b/arch/sh/kernel/timers/timer-tmu.c index 3c61ddd4d43e..0db3f9510336 100644 --- a/arch/sh/kernel/timers/timer-tmu.c +++ b/arch/sh/kernel/timers/timer-tmu.c @@ -263,7 +263,7 @@ static int tmu_timer_init(void) tmu0_clockevent.min_delta_ns = clockevent_delta2ns(1, &tmu0_clockevent); - tmu0_clockevent.cpumask = cpumask_of_cpu(0); + tmu0_clockevent.cpumask = cpumask_of(0); clockevents_register_device(&tmu0_clockevent); diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index 141da3759091..9df8f095a8b1 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -763,7 +763,7 @@ void __devinit setup_sparc64_timer(void) sevt = &__get_cpu_var(sparc64_events); memcpy(sevt, &sparc64_clockevent, sizeof(*sevt)); - sevt->cpumask = cpumask_of_cpu(smp_processor_id()); + sevt->cpumask = cpumask_of(smp_processor_id()); clockevents_register_device(sevt); } diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c index 47f04f4a3464..b13a87a3ec95 100644 --- a/arch/um/kernel/time.c +++ b/arch/um/kernel/time.c @@ -50,7 +50,7 @@ static int itimer_next_event(unsigned long delta, static struct clock_event_device itimer_clockevent = { .name = "itimer", .rating = 250, - .cpumask = CPU_MASK_ALL, + .cpumask = cpu_all_mask, .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .set_mode = itimer_set_mode, .set_next_event = itimer_next_event, diff --git a/arch/x86/kernel/apic.c b/arch/x86/kernel/apic.c index 16f94879b525..b2cef49f3085 100644 --- a/arch/x86/kernel/apic.c +++ b/arch/x86/kernel/apic.c @@ -141,7 +141,7 @@ static int lapic_next_event(unsigned long delta, struct clock_event_device *evt); static void lapic_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt); -static void lapic_timer_broadcast(cpumask_t mask); +static void lapic_timer_broadcast(const struct cpumask *mask); static void apic_pm_activate(void); /* @@ -453,10 +453,10 @@ static void lapic_timer_setup(enum clock_event_mode mode, /* * Local APIC timer broadcast function */ -static void lapic_timer_broadcast(cpumask_t mask) +static void lapic_timer_broadcast(const struct cpumask *mask) { #ifdef CONFIG_SMP - send_IPI_mask(mask, LOCAL_TIMER_VECTOR); + send_IPI_mask(*mask, LOCAL_TIMER_VECTOR); #endif } @@ -469,7 +469,7 @@ static void __cpuinit setup_APIC_timer(void) struct clock_event_device *levt = &__get_cpu_var(lapic_events); memcpy(levt, &lapic_clockevent, sizeof(*levt)); - levt->cpumask = cpumask_of_cpu(smp_processor_id()); + levt->cpumask = cpumask_of(smp_processor_id()); clockevents_register_device(levt); } diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 940f25851e1e..e76d7e272974 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -246,7 +246,7 @@ static void hpet_legacy_clockevent_register(void) * Start hpet with the boot cpu mask and make it * global after the IO_APIC has been initialized. */ - hpet_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); + hpet_clockevent.cpumask = cpumask_of(smp_processor_id()); clockevents_register_device(&hpet_clockevent); global_clock_event = &hpet_clockevent; printk(KERN_DEBUG "hpet clockevent registered\n"); @@ -500,7 +500,7 @@ static void init_one_hpet_msi_clockevent(struct hpet_dev *hdev, int cpu) /* 5 usec minimum reprogramming delta. */ evt->min_delta_ns = 5000; - evt->cpumask = cpumask_of_cpu(hdev->cpu); + evt->cpumask = cpumask_of(hdev->cpu); clockevents_register_device(evt); } diff --git a/arch/x86/kernel/i8253.c b/arch/x86/kernel/i8253.c index c1b5e3ece1f2..10f92fb532f3 100644 --- a/arch/x86/kernel/i8253.c +++ b/arch/x86/kernel/i8253.c @@ -114,7 +114,7 @@ void __init setup_pit_timer(void) * Start pit with the boot cpu mask and make it global after the * IO_APIC has been initialized. */ - pit_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); + pit_clockevent.cpumask = cpumask_of(smp_processor_id()); pit_clockevent.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, pit_clockevent.shift); pit_clockevent.max_delta_ns = diff --git a/arch/x86/kernel/mfgpt_32.c b/arch/x86/kernel/mfgpt_32.c index 3b599518c322..c12314c9e86f 100644 --- a/arch/x86/kernel/mfgpt_32.c +++ b/arch/x86/kernel/mfgpt_32.c @@ -287,7 +287,7 @@ static struct clock_event_device mfgpt_clockevent = { .set_mode = mfgpt_set_mode, .set_next_event = mfgpt_next_event, .rating = 250, - .cpumask = CPU_MASK_ALL, + .cpumask = cpu_all_mask, .shift = 32 }; diff --git a/arch/x86/kernel/vmiclock_32.c b/arch/x86/kernel/vmiclock_32.c index 254ee07f8635..c4c1f9e09402 100644 --- a/arch/x86/kernel/vmiclock_32.c +++ b/arch/x86/kernel/vmiclock_32.c @@ -226,7 +226,7 @@ static void __devinit vmi_time_init_clockevent(void) /* Upper bound is clockevent's use of ulong for cycle deltas. */ evt->max_delta_ns = clockevent_delta2ns(ULONG_MAX, evt); evt->min_delta_ns = clockevent_delta2ns(1, evt); - evt->cpumask = cpumask_of_cpu(cpu); + evt->cpumask = cpumask_of(cpu); printk(KERN_WARNING "vmi: registering clock event %s. mult=%lu shift=%u\n", evt->name, evt->mult, evt->shift); diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index a5d8e1ace1cf..104c8220a383 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -737,7 +737,7 @@ static void lguest_time_init(void) /* We can't set cpumask in the initializer: damn C limitations! Set it * here and register our timer device. */ - lguest_clockevent.cpumask = cpumask_of_cpu(0); + lguest_clockevent.cpumask = cpumask_of(0); clockevents_register_device(&lguest_clockevent); /* Finally, we unblock the timer interrupt. */ diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index c9f7cda48ed7..65d75a6be0ba 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -437,7 +437,7 @@ void xen_setup_timer(int cpu) evt = &per_cpu(xen_clock_events, cpu); memcpy(evt, xen_clockevent, sizeof(*evt)); - evt->cpumask = cpumask_of_cpu(cpu); + evt->cpumask = cpumask_of(cpu); evt->irq = irq; setup_runstate_info(cpu); diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index f450588e5858..254f1064d973 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -154,7 +154,6 @@ static struct tc_clkevt_device clkevt = { .shift = 32, /* Should be lower than at91rm9200's system timer */ .rating = 125, - .cpumask = CPU_MASK_CPU0, .set_next_event = tc_next_event, .set_mode = tc_mode, }, @@ -195,6 +194,7 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) clkevt.clkevt.max_delta_ns = clockevent_delta2ns(0xffff, &clkevt.clkevt); clkevt.clkevt.min_delta_ns = clockevent_delta2ns(1, &clkevt.clkevt) + 1; + clkevt.clkevt.cpumask = cpumask_of(0); setup_irq(irq, &tc_irqaction); diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index ed3a5d473e52..cea153697ec7 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -82,13 +82,13 @@ struct clock_event_device { int shift; int rating; int irq; - cpumask_t cpumask; + const struct cpumask *cpumask; int (*set_next_event)(unsigned long evt, struct clock_event_device *); void (*set_mode)(enum clock_event_mode mode, struct clock_event_device *); void (*event_handler)(struct clock_event_device *); - void (*broadcast)(cpumask_t mask); + void (*broadcast)(const struct cpumask *mask); struct list_head list; enum clock_event_mode mode; ktime_t next_event; diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index f8d968063cea..ea2f48af83cf 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -166,6 +166,8 @@ static void clockevents_notify_released(void) void clockevents_register_device(struct clock_event_device *dev) { BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED); + BUG_ON(!dev->cpumask); + /* * A nsec2cyc multiplicator of 0 is invalid and we'd crash * on it, so fix it up and emit a warning: diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index f98a1b7b16e9..9590af2327be 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -150,7 +150,7 @@ static void tick_do_broadcast(cpumask_t mask) */ cpu = first_cpu(mask); td = &per_cpu(tick_cpu_device, cpu); - td->evtdev->broadcast(mask); + td->evtdev->broadcast(&mask); } } diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index ab65d217583f..f8372be74122 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -171,7 +171,7 @@ static void tick_setup_device(struct tick_device *td, * When the device is not per cpu, pin the interrupt to the * current cpu: */ - if (!cpumask_equal(&newdev->cpumask, cpumask)) + if (!cpumask_equal(newdev->cpumask, cpumask)) irq_set_affinity(newdev->irq, cpumask); /* @@ -202,14 +202,14 @@ static int tick_check_new_device(struct clock_event_device *newdev) spin_lock_irqsave(&tick_device_lock, flags); cpu = smp_processor_id(); - if (!cpu_isset(cpu, newdev->cpumask)) + if (!cpumask_test_cpu(cpu, newdev->cpumask)) goto out_bc; td = &per_cpu(tick_cpu_device, cpu); curdev = td->evtdev; /* cpu local device ? */ - if (!cpus_equal(newdev->cpumask, cpumask_of_cpu(cpu))) { + if (!cpumask_equal(newdev->cpumask, cpumask_of(cpu))) { /* * If the cpu affinity of the device interrupt can not @@ -222,7 +222,7 @@ static int tick_check_new_device(struct clock_event_device *newdev) * If we have a cpu local device already, do not replace it * by a non cpu local device */ - if (curdev && cpus_equal(curdev->cpumask, cpumask_of_cpu(cpu))) + if (curdev && cpumask_equal(curdev->cpumask, cpumask_of(cpu))) goto out_bc; } -- cgit v1.2.3 From 7be7585393d311866653564fbcd10a3232773c0b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 13 Dec 2008 21:20:28 +1030 Subject: cpumask: Use all NR_CPUS bits unless CONFIG_CPUMASK_OFFSTACK Impact: futureproof as we convert more code to new APIs The old cpumask operators treat all NR_CPUS bits as relevent, the new ones use nr_cpumask_bits. For large NR_CPUS and small nr_cpu_ids, this makes a difference. However, mixing the two can cause problems with undefined bits. An arch which sets CONFIG_CPUMASK_OFFSTACK should have converted across to the new operators, so it's safe in that case. (Thanks to Stephen Rothwell for bisecting the initial unused-bits bug, and Mike Travis for this solution). Signed-off-by: Rusty Russell Cc: Mike Travis --- include/linux/cpumask.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 94a2ab88ae85..d4bf52603e6b 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -510,9 +510,6 @@ extern cpumask_t cpu_active_map; [BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD \ } -/* This produces more efficient code. */ -#define nr_cpumask_bits NR_CPUS - #else /* NR_CPUS > BITS_PER_LONG */ #define CPU_BITS_ALL \ @@ -520,9 +517,15 @@ extern cpumask_t cpu_active_map; [0 ... BITS_TO_LONGS(NR_CPUS)-2] = ~0UL, \ [BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD \ } +#endif /* NR_CPUS > BITS_PER_LONG */ +#ifdef CONFIG_CPUMASK_OFFSTACK +/* Assuming NR_CPUS is huge, a runtime limit is more efficient. Also, + * not all bits may be allocated. */ #define nr_cpumask_bits nr_cpu_ids -#endif /* NR_CPUS > BITS_PER_LONG */ +#else +#define nr_cpumask_bits NR_CPUS +#endif /* verify cpu argument to cpumask_* operators */ static inline unsigned int cpumask_check(unsigned int cpu) -- cgit v1.2.3 From 7b4967c532045a1983d6d4af5c69cc7c5109f62b Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Fri, 19 Dec 2008 16:56:37 +1030 Subject: cpumask: Add alloc_cpumask_var_node() Impact: New API This will be needed in x86 code to allocate the domain and old_domain cpumasks on the same node as where the containing irq_cfg struct is allocated. (Also fixes double-dump_stack on rare CONFIG_DEBUG_PER_CPU_MAPS case) Signed-off-by: Mike Travis Signed-off-by: Rusty Russell (re-impl alloc_cpumask_var) --- include/linux/cpumask.h | 7 +++++++ lib/cpumask.c | 11 ++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index d4bf52603e6b..b5ad19a6f43f 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -1025,6 +1025,7 @@ static inline size_t cpumask_size(void) #ifdef CONFIG_CPUMASK_OFFSTACK typedef struct cpumask *cpumask_var_t; +bool alloc_cpumask_var_node(cpumask_var_t *mask, gfp_t flags, int node); bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags); void alloc_bootmem_cpumask_var(cpumask_var_t *mask); void free_cpumask_var(cpumask_var_t mask); @@ -1038,6 +1039,12 @@ static inline bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags) return true; } +static inline bool alloc_cpumask_var_node(cpumask_var_t *mask, gfp_t flags, + int node) +{ + return true; +} + static inline void alloc_bootmem_cpumask_var(cpumask_var_t *mask) { } diff --git a/lib/cpumask.c b/lib/cpumask.c index 8d03f22c6ced..3f258f58c85b 100644 --- a/lib/cpumask.c +++ b/lib/cpumask.c @@ -76,15 +76,14 @@ int cpumask_any_but(const struct cpumask *mask, unsigned int cpu) /* These are not inline because of header tangles. */ #ifdef CONFIG_CPUMASK_OFFSTACK -bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags) +bool alloc_cpumask_var_node(cpumask_var_t *mask, gfp_t flags, int node) { if (likely(slab_is_available())) - *mask = kmalloc(cpumask_size(), flags); + *mask = kmalloc_node(cpumask_size(), flags, node); else { #ifdef CONFIG_DEBUG_PER_CPU_MAPS printk(KERN_ERR "=> alloc_cpumask_var: kmalloc not available!\n"); - dump_stack(); #endif *mask = NULL; } @@ -96,6 +95,12 @@ bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags) #endif return *mask != NULL; } +EXPORT_SYMBOL(alloc_cpumask_var_node); + +bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags) +{ + return alloc_cpumask_var_node(mask, flags, numa_node_id()); +} EXPORT_SYMBOL(alloc_cpumask_var); void __init alloc_bootmem_cpumask_var(cpumask_var_t *mask) -- cgit v1.2.3 From e057d7aea9d8f2a46cd440d8bfb72245d4e72d79 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Mon, 15 Dec 2008 20:26:48 -0800 Subject: cpumask: add sysfs displays for configured and disabled cpu maps Impact: add new sysfs files. Add sysfs files "kernel_max" and "offline" to display the max CPU index allowed (NR_CPUS-1), and the map of cpus that are offline. Cpus can be offlined via HOTPLUG, disabled by the BIOS ACPI tables, or if they exceed the number of cpus allowed by the NR_CPUS config option, or the "maxcpus=NUM" kernel start parameter. The "possible_cpus=NUM" parameter can also extend the number of possible cpus allowed, in which case the cpus not present at startup will be in the offline state. (These cpus can be HOTPLUGGED ON after system startup [pending a follow-on patch to provide the capability via the /sys/devices/sys/cpu/cpuN/online mechanism to bring them online.]) By design, the "offlined cpus > possible cpus" display will always use the following formats: * all possible cpus online: "x$" or "x-y$" * some possible cpus offline: ".*,x$" or ".*,x-y$" where: x == number of possible cpus (nr_cpu_ids); and y == number of cpus >= NR_CPUS or maxcpus (if y > x). One use of this feature is for distros to select (or configure) the appropriate kernel to install for the resident system. Notes: * cpus offlined <= possible cpus will be printed for all architectures. * cpus offlined > possible cpus will only be printed for arches that set 'total_cpus' [X86 only in this patch]. Based on tip/cpus4096 + .../rusty/linux-2.6-for-ingo.git/master + x86-only-patches sent 12/15. Signed-off-by: Mike Travis Signed-off-by: Rusty Russell --- drivers/base/cpu.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/smp.h | 3 +++ 2 files changed, 47 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 4259072f5bd0..2aef96f20b30 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -128,10 +128,54 @@ print_cpus_func(online); print_cpus_func(possible); print_cpus_func(present); +/* + * Print values for NR_CPUS and offlined cpus + */ +static ssize_t print_cpus_kernel_max(struct sysdev_class *class, char *buf) +{ + int n = snprintf(buf, PAGE_SIZE-2, "%d\n", CONFIG_NR_CPUS - 1); + return n; +} +static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL); + +/* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */ +unsigned int total_cpus; + +static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf) +{ + int n = 0, len = PAGE_SIZE-2; + cpumask_var_t offline; + + /* display offline cpus < nr_cpu_ids */ + if (!alloc_cpumask_var(&offline, GFP_KERNEL)) + return -ENOMEM; + cpumask_complement(offline, cpu_online_mask); + n = cpulist_scnprintf(buf, len, offline); + free_cpumask_var(offline); + + /* display offline cpus >= nr_cpu_ids */ + if (total_cpus && nr_cpu_ids < total_cpus) { + if (n && n < len) + buf[n++] = ','; + + if (nr_cpu_ids == total_cpus-1) + n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids); + else + n += snprintf(&buf[n], len - n, "%d-%d", + nr_cpu_ids, total_cpus-1); + } + + n += snprintf(&buf[n], len - n, "\n"); + return n; +} +static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL); + static struct sysdev_class_attribute *cpu_state_attr[] = { &attr_online_map, &attr_possible_map, &attr_present_map, + &attr_kernel_max, + &attr_offline, }; static int cpu_states_init(void) diff --git a/include/linux/smp.h b/include/linux/smp.h index 3f9a60043a97..0d5770c2e43a 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -21,6 +21,9 @@ struct call_single_data { u16 priv; }; +/* total number of cpus in this system (may exceed NR_CPUS) */ +extern unsigned int total_cpus; + #ifdef CONFIG_SMP #include -- cgit v1.2.3 From 716707b29906e1d8d190defe3d646610b097a861 Mon Sep 17 00:00:00 2001 From: Vaidyanathan Srinivasan Date: Thu, 18 Dec 2008 23:26:02 +0530 Subject: sched: convert BALANCE_FOR_xx_POWER to inline functions Impact: cleanup BALANCE_FOR_MC_POWER and similar macros defined in sched.h are not constants and have various condition checks and significant amount of code that is not suitable to be contain in a macro. Also there could be side effects on the expressions passed to some of them like test_sd_parent(). This patch converts all complex macros related to power savings balance to inline functions. Signed-off-by: Vaidyanathan Srinivasan Acked-by: Balbir Singh Acked-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/sched.h | 33 ++++++++++++++++++++++++--------- include/linux/topology.h | 4 ++-- 2 files changed, 26 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 4240f6bfa812..1210fb0e45ff 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -763,15 +763,23 @@ enum cpu_idle_type { #define SD_SERIALIZE 1024 /* Only a single load balancing instance */ #define SD_WAKE_IDLE_FAR 2048 /* Gain latency sacrificing cache hit */ -#define BALANCE_FOR_MC_POWER \ - (sched_smt_power_savings ? SD_POWERSAVINGS_BALANCE : 0) +extern int sched_mc_power_savings, sched_smt_power_savings; + +static inline int sd_balance_for_mc_power(void) +{ + if (sched_smt_power_savings) + return SD_POWERSAVINGS_BALANCE; -#define BALANCE_FOR_PKG_POWER \ - ((sched_mc_power_savings || sched_smt_power_savings) ? \ - SD_POWERSAVINGS_BALANCE : 0) + return 0; +} -#define test_sd_parent(sd, flag) ((sd->parent && \ - (sd->parent->flags & flag)) ? 1 : 0) +static inline int sd_balance_for_package_power(void) +{ + if (sched_mc_power_savings | sched_smt_power_savings) + return SD_POWERSAVINGS_BALANCE; + + return 0; +} struct sched_group { @@ -1399,6 +1407,15 @@ struct task_struct { #endif }; +/* Test a flag in parent sched domain */ +static inline int test_sd_parent(struct sched_domain *sd, int flag) +{ + if (sd->parent && (sd->parent->flags & flag)) + return 1; + + return 0; +} + /* * Priority of a process goes from 0..MAX_PRIO-1, valid RT * priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH @@ -2256,8 +2273,6 @@ __trace_special(void *__tr, void *__data, extern long sched_setaffinity(pid_t pid, const struct cpumask *new_mask); extern long sched_getaffinity(pid_t pid, struct cpumask *mask); -extern int sched_mc_power_savings, sched_smt_power_savings; - extern void normalize_rt_tasks(void); #ifdef CONFIG_GROUP_SCHED diff --git a/include/linux/topology.h b/include/linux/topology.h index 0c5b5ac36d8e..0ce7c0dac06c 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -125,7 +125,7 @@ int arch_update_cpu_topology(void); | SD_WAKE_AFFINE \ | SD_WAKE_BALANCE \ | SD_SHARE_PKG_RESOURCES\ - | BALANCE_FOR_MC_POWER, \ + | sd_balance_for_mc_power(),\ .last_balance = jiffies, \ .balance_interval = 1, \ } @@ -150,7 +150,7 @@ int arch_update_cpu_topology(void); | SD_BALANCE_FORK \ | SD_WAKE_AFFINE \ | SD_WAKE_BALANCE \ - | BALANCE_FOR_PKG_POWER,\ + | sd_balance_for_package_power(),\ .last_balance = jiffies, \ .balance_interval = 1, \ } -- cgit v1.2.3 From afb8a9b70b86866a60e08b2956ae4e1406390336 Mon Sep 17 00:00:00 2001 From: Gautham R Shenoy Date: Thu, 18 Dec 2008 23:26:09 +0530 Subject: sched: framework for sched_mc/smt_power_savings=N Impact: extend range of /sys/devices/system/cpu/sched_mc_power_savings Currently the sched_mc/smt_power_savings variable is a boolean, which either enables or disables topology based power savings. This patch extends the behaviour of the variable from boolean to multivalued, such that based on the value, we decide how aggressively do we want to perform powersavings balance at appropriate sched domain based on topology. Variable levels of power saving tunable would benefit end user to match the required level of power savings vs performance trade-off depending on the system configuration and workloads. This version makes the sched_mc_power_savings global variable to take more values (0,1,2). Later versions can have a single tunable called sched_power_savings instead of sched_{mc,smt}_power_savings. Signed-off-by: Gautham R Shenoy Signed-off-by: Vaidyanathan Srinivasan Acked-by: Balbir Singh Acked-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/sched.h | 11 +++++++++++ kernel/sched.c | 17 ++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 1210fb0e45ff..a96726658eca 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -763,6 +763,17 @@ enum cpu_idle_type { #define SD_SERIALIZE 1024 /* Only a single load balancing instance */ #define SD_WAKE_IDLE_FAR 2048 /* Gain latency sacrificing cache hit */ +enum powersavings_balance_level { + POWERSAVINGS_BALANCE_NONE = 0, /* No power saving load balance */ + POWERSAVINGS_BALANCE_BASIC, /* Fill one thread/core/package + * first for long running threads + */ + POWERSAVINGS_BALANCE_WAKEUP, /* Also bias task wakeups to semi-idle + * cpu package for power savings + */ + MAX_POWERSAVINGS_BALANCE_LEVELS +}; + extern int sched_mc_power_savings, sched_smt_power_savings; static inline int sd_balance_for_mc_power(void) diff --git a/kernel/sched.c b/kernel/sched.c index b309027bf9e8..56b285cd5350 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -7906,14 +7906,25 @@ int arch_reinit_sched_domains(void) static ssize_t sched_power_savings_store(const char *buf, size_t count, int smt) { int ret; + unsigned int level = 0; - if (buf[0] != '0' && buf[0] != '1') + if (sscanf(buf, "%u", &level) != 1) + return -EINVAL; + + /* + * level is always be positive so don't check for + * level < POWERSAVINGS_BALANCE_NONE which is 0 + * What happens on 0 or 1 byte write, + * need to check for count as well? + */ + + if (level >= MAX_POWERSAVINGS_BALANCE_LEVELS) return -EINVAL; if (smt) - sched_smt_power_savings = (buf[0] == '1'); + sched_smt_power_savings = level; else - sched_mc_power_savings = (buf[0] == '1'); + sched_mc_power_savings = level; ret = arch_reinit_sched_domains(); -- cgit v1.2.3 From 100fdaee70ebf5f31b9451fbc01300c627091328 Mon Sep 17 00:00:00 2001 From: Vaidyanathan Srinivasan Date: Thu, 18 Dec 2008 23:26:47 +0530 Subject: sched: add SD_BALANCE_NEWIDLE at MC and CPU level for sched_mc>0 Impact: change task balancing to save power more agressively Add SD_BALANCE_NEWIDLE flag at MC level and CPU level if sched_mc is set. This helps power savings and will not affect performance when sched_mc=0 Ingo and Mike Galbraith have optimised the SD flags by removing SD_BALANCE_NEWIDLE at MC and CPU level. This helps performance but hurts power savings since this slows down task consolidation by reducing the number of times load_balance is run. sched: fine-tune SD_MC_INIT commit 14800984706bf6936bbec5187f736e928be5c218 Author: Mike Galbraith Date: Fri Nov 7 15:26:50 2008 +0100 sched: re-tune balancing -- revert commit 9fcd18c9e63e325dbd2b4c726623f760788d5aa8 Author: Ingo Molnar Date: Wed Nov 5 16:52:08 2008 +0100 This patch selectively enables SD_BALANCE_NEWIDLE flag only when sched_mc is set to 1 or 2. This helps power savings by task consolidation and also does not hurt performance at sched_mc=0 where all power saving optimisations are turned off. Signed-off-by: Vaidyanathan Srinivasan Acked-by: Balbir Singh Acked-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- include/linux/sched.h | 13 +++++++++++++ include/linux/topology.h | 6 ++++-- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index a96726658eca..5a933d925473 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -792,6 +792,19 @@ static inline int sd_balance_for_package_power(void) return 0; } +/* + * Optimise SD flags for power savings: + * SD_BALANCE_NEWIDLE helps agressive task consolidation and power savings. + * Keep default SD flags if sched_{smt,mc}_power_saving=0 + */ + +static inline int sd_power_saving_flags(void) +{ + if (sched_mc_power_savings | sched_smt_power_savings) + return SD_BALANCE_NEWIDLE; + + return 0; +} struct sched_group { struct sched_group *next; /* Must be a circular list */ diff --git a/include/linux/topology.h b/include/linux/topology.h index 0ce7c0dac06c..e632d29f0544 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -125,7 +125,8 @@ int arch_update_cpu_topology(void); | SD_WAKE_AFFINE \ | SD_WAKE_BALANCE \ | SD_SHARE_PKG_RESOURCES\ - | sd_balance_for_mc_power(),\ + | sd_balance_for_mc_power()\ + | sd_power_saving_flags(),\ .last_balance = jiffies, \ .balance_interval = 1, \ } @@ -150,7 +151,8 @@ int arch_update_cpu_topology(void); | SD_BALANCE_FORK \ | SD_WAKE_AFFINE \ | SD_WAKE_BALANCE \ - | sd_balance_for_package_power(),\ + | sd_balance_for_package_power()\ + | sd_power_saving_flags(),\ .last_balance = jiffies, \ .balance_interval = 1, \ } -- cgit v1.2.3 From 06aaf76a7e2e4cc57eabcb8f43ec99c961fe55fe Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 18 Dec 2008 21:30:23 +0100 Subject: sched: move test_sd_parent() to an SMP section of sched.h Impact: build fix Signed-off-by: Ingo Molnar --- include/linux/sched.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 5a933d925473..e5f928a079e8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -920,6 +920,15 @@ extern void partition_sched_domains(int ndoms_new, struct cpumask *doms_new, struct sched_domain_attr *dattr_new); extern int arch_reinit_sched_domains(void); +/* Test a flag in parent sched domain */ +static inline int test_sd_parent(struct sched_domain *sd, int flag) +{ + if (sd->parent && (sd->parent->flags & flag)) + return 1; + + return 0; +} + #else /* CONFIG_SMP */ struct sched_domain_attr; @@ -1431,15 +1440,6 @@ struct task_struct { #endif }; -/* Test a flag in parent sched domain */ -static inline int test_sd_parent(struct sched_domain *sd, int flag) -{ - if (sd->parent && (sd->parent->flags & flag)) - return 1; - - return 0; -} - /* * Priority of a process goes from 0..MAX_PRIO-1, valid RT * priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH -- cgit v1.2.3 From a01777ecf227de735d7e525ecda48fe74b838a17 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 22 Dec 2008 18:30:29 +0000 Subject: uwb: remove unused include/linux/uwb/debug.h Signed-off-by: David Vrabel --- drivers/uwb/address.c | 2 +- drivers/uwb/driver.c | 2 +- drivers/uwb/i1480/i1480u-wlp/lc.c | 2 +- drivers/uwb/i1480/i1480u-wlp/netdev.c | 2 +- drivers/uwb/i1480/i1480u-wlp/sysfs.c | 2 +- include/linux/uwb/debug.h | 82 ----------------------------------- 6 files changed, 5 insertions(+), 87 deletions(-) delete mode 100644 include/linux/uwb/debug.h (limited to 'include/linux') diff --git a/drivers/uwb/address.c b/drivers/uwb/address.c index 1664ae5f1706..ad21b1d7218c 100644 --- a/drivers/uwb/address.c +++ b/drivers/uwb/address.c @@ -28,7 +28,7 @@ #include #include #include -#include + #include "uwb-internal.h" diff --git a/drivers/uwb/driver.c b/drivers/uwb/driver.c index f57c26580de2..da77e41de990 100644 --- a/drivers/uwb/driver.c +++ b/drivers/uwb/driver.c @@ -53,7 +53,7 @@ #include #include #include -#include + #include "uwb-internal.h" diff --git a/drivers/uwb/i1480/i1480u-wlp/lc.c b/drivers/uwb/i1480/i1480u-wlp/lc.c index 488b2e30a0a8..049c05d4cc6a 100644 --- a/drivers/uwb/i1480/i1480u-wlp/lc.c +++ b/drivers/uwb/i1480/i1480u-wlp/lc.c @@ -57,7 +57,7 @@ */ #include #include -#include + #include "i1480u-wlp.h" diff --git a/drivers/uwb/i1480/i1480u-wlp/netdev.c b/drivers/uwb/i1480/i1480u-wlp/netdev.c index 2eafb973cd05..e3873ffb942c 100644 --- a/drivers/uwb/i1480/i1480u-wlp/netdev.c +++ b/drivers/uwb/i1480/i1480u-wlp/netdev.c @@ -41,7 +41,7 @@ #include #include -#include + #include "i1480u-wlp.h" struct i1480u_cmd_set_ip_mas { diff --git a/drivers/uwb/i1480/i1480u-wlp/sysfs.c b/drivers/uwb/i1480/i1480u-wlp/sysfs.c index a92a787725fc..4ffaf546cc6c 100644 --- a/drivers/uwb/i1480/i1480u-wlp/sysfs.c +++ b/drivers/uwb/i1480/i1480u-wlp/sysfs.c @@ -25,8 +25,8 @@ #include #include -#include #include + #include "i1480u-wlp.h" diff --git a/include/linux/uwb/debug.h b/include/linux/uwb/debug.h deleted file mode 100644 index 67a240527145..000000000000 --- a/include/linux/uwb/debug.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Ultra Wide Band - * Debug Support - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: doc - * Invoke like: - * - * #define D_LOCAL 4 - * #include - * - * At the end of your include files. - */ -#include - -struct device; -extern void dump_bytes(struct device *dev, const void *_buf, size_t rsize); - -/* Master debug switch; !0 enables, 0 disables */ -#define D_MASTER (!0) - -/* Local (per-file) debug switch; #define before #including */ -#ifndef D_LOCAL -#define D_LOCAL 0 -#endif - -#undef __d_printf -#undef d_fnstart -#undef d_fnend -#undef d_printf -#undef d_dump - -#define __d_printf(l, _tag, _dev, f, a...) \ -do { \ - struct device *__dev = (_dev); \ - if (D_MASTER && D_LOCAL >= (l)) { \ - char __head[64] = ""; \ - if (_dev != NULL) { \ - if ((unsigned long)__dev < 4096) \ - printk(KERN_ERR "E: Corrupt dev %p\n", \ - __dev); \ - else \ - snprintf(__head, sizeof(__head), \ - "%s %s: ", \ - dev_driver_string(__dev), \ - dev_name(__dev)); \ - } \ - printk(KERN_ERR "%s%s" _tag ": " f, __head, \ - __func__, ## a); \ - } \ -} while (0 && _dev) - -#define d_fnstart(l, _dev, f, a...) \ - __d_printf(l, " FNSTART", _dev, f, ## a) -#define d_fnend(l, _dev, f, a...) \ - __d_printf(l, " FNEND", _dev, f, ## a) -#define d_printf(l, _dev, f, a...) \ - __d_printf(l, "", _dev, f, ## a) -#define d_dump(l, _dev, ptr, size) \ -do { \ - struct device *__dev = _dev; \ - if (D_MASTER && D_LOCAL >= (l)) \ - dump_bytes(__dev, ptr, size); \ -} while (0 && _dev) -#define d_test(l) (D_MASTER && D_LOCAL >= (l)) -- cgit v1.2.3 From 278d1ed65e25d80af7c3a112d707b3f70516ddb4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Dec 2008 09:05:12 +1030 Subject: cpumask: make CONFIG_NR_CPUS always valid. Impact: cleanup Currently we have NR_CPUS, which is 1 on UP, and CONFIG_NR_CPUS on SMP. If we make CONFIG_NR_CPUS always valid (and always 1 on !SMP), we can skip the middleman. This also allows us to find and check all the unaudited NR_CPUS usage as we prepare for v. large NR_CPUS. To avoid breaking every arch, we cheat and do this for the moment in the header if the arch doesn't. Signed-off-by: Rusty Russell Signed-off-by: Mike Travis --- include/linux/threads.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/threads.h b/include/linux/threads.h index 38d1a5d6568e..052b12bec8bd 100644 --- a/include/linux/threads.h +++ b/include/linux/threads.h @@ -8,17 +8,17 @@ */ /* - * Maximum supported processors that can run under SMP. This value is - * set via configure setting. The maximum is equal to the size of the - * bitmasks used on that platform, i.e. 32 or 64. Setting this smaller - * saves quite a bit of memory. + * Maximum supported processors. Setting this smaller saves quite a + * bit of memory. Use nr_cpu_ids instead of this except for static bitmaps. */ -#ifdef CONFIG_SMP -#define NR_CPUS CONFIG_NR_CPUS -#else -#define NR_CPUS 1 +#ifndef CONFIG_NR_CPUS +/* FIXME: This should be fixed in the arch's Kconfig */ +#define CONFIG_NR_CPUS 1 #endif +/* Places which use this should consider cpumask_var_t. */ +#define NR_CPUS CONFIG_NR_CPUS + #define MIN_THREADS_LEFT_FOR_ROOT 4 /* -- cgit v1.2.3 From 4b0bc0bca83f3fb7cf920e2ec80684c15d2269c0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Dec 2008 09:05:13 +1030 Subject: bitmap: test for constant as well as small size for inline versions Impact: reduce text size bitmap_zero et al have a fastpath for nbits <= BITS_PER_LONG, but this should really only apply where the nbits is known at compile time. This only saves about 1200 bytes on an allyesconfig kernel, but with cpumasks going variable that number will increase. text data bss dec hex filename 35327852 5035607 6782976 47146435 2cf65c3 vmlinux-before 35326640 5035607 6782976 47145223 2cf6107 vmlinux-after Signed-off-by: Rusty Russell --- include/linux/bitmap.h | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index a08c33a26ca9..2878811c6134 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -137,9 +137,12 @@ extern void bitmap_copy_le(void *dst, const unsigned long *src, int nbits); (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \ ) +#define small_const_nbits(nbits) \ + (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG) + static inline void bitmap_zero(unsigned long *dst, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) *dst = 0UL; else { int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); @@ -150,7 +153,7 @@ static inline void bitmap_zero(unsigned long *dst, int nbits) static inline void bitmap_fill(unsigned long *dst, int nbits) { size_t nlongs = BITS_TO_LONGS(nbits); - if (nlongs > 1) { + if (!small_const_nbits(nbits)) { int len = (nlongs - 1) * sizeof(unsigned long); memset(dst, 0xff, len); } @@ -160,7 +163,7 @@ static inline void bitmap_fill(unsigned long *dst, int nbits) static inline void bitmap_copy(unsigned long *dst, const unsigned long *src, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) *dst = *src; else { int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); @@ -171,7 +174,7 @@ static inline void bitmap_copy(unsigned long *dst, const unsigned long *src, static inline void bitmap_and(unsigned long *dst, const unsigned long *src1, const unsigned long *src2, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) *dst = *src1 & *src2; else __bitmap_and(dst, src1, src2, nbits); @@ -180,7 +183,7 @@ static inline void bitmap_and(unsigned long *dst, const unsigned long *src1, static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, const unsigned long *src2, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) *dst = *src1 | *src2; else __bitmap_or(dst, src1, src2, nbits); @@ -189,7 +192,7 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1, const unsigned long *src2, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) *dst = *src1 ^ *src2; else __bitmap_xor(dst, src1, src2, nbits); @@ -198,7 +201,7 @@ static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1, static inline void bitmap_andnot(unsigned long *dst, const unsigned long *src1, const unsigned long *src2, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) *dst = *src1 & ~(*src2); else __bitmap_andnot(dst, src1, src2, nbits); @@ -207,7 +210,7 @@ static inline void bitmap_andnot(unsigned long *dst, const unsigned long *src1, static inline void bitmap_complement(unsigned long *dst, const unsigned long *src, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) *dst = ~(*src) & BITMAP_LAST_WORD_MASK(nbits); else __bitmap_complement(dst, src, nbits); @@ -216,7 +219,7 @@ static inline void bitmap_complement(unsigned long *dst, const unsigned long *sr static inline int bitmap_equal(const unsigned long *src1, const unsigned long *src2, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits)); else return __bitmap_equal(src1, src2, nbits); @@ -225,7 +228,7 @@ static inline int bitmap_equal(const unsigned long *src1, static inline int bitmap_intersects(const unsigned long *src1, const unsigned long *src2, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0; else return __bitmap_intersects(src1, src2, nbits); @@ -234,7 +237,7 @@ static inline int bitmap_intersects(const unsigned long *src1, static inline int bitmap_subset(const unsigned long *src1, const unsigned long *src2, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) return ! ((*src1 & ~(*src2)) & BITMAP_LAST_WORD_MASK(nbits)); else return __bitmap_subset(src1, src2, nbits); @@ -242,7 +245,7 @@ static inline int bitmap_subset(const unsigned long *src1, static inline int bitmap_empty(const unsigned long *src, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) return ! (*src & BITMAP_LAST_WORD_MASK(nbits)); else return __bitmap_empty(src, nbits); @@ -250,7 +253,7 @@ static inline int bitmap_empty(const unsigned long *src, int nbits) static inline int bitmap_full(const unsigned long *src, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits)); else return __bitmap_full(src, nbits); @@ -258,7 +261,7 @@ static inline int bitmap_full(const unsigned long *src, int nbits) static inline int bitmap_weight(const unsigned long *src, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits)); return __bitmap_weight(src, nbits); } @@ -266,7 +269,7 @@ static inline int bitmap_weight(const unsigned long *src, int nbits) static inline void bitmap_shift_right(unsigned long *dst, const unsigned long *src, int n, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) *dst = *src >> n; else __bitmap_shift_right(dst, src, n, nbits); @@ -275,7 +278,7 @@ static inline void bitmap_shift_right(unsigned long *dst, static inline void bitmap_shift_left(unsigned long *dst, const unsigned long *src, int n, int nbits) { - if (nbits <= BITS_PER_LONG) + if (small_const_nbits(nbits)) *dst = (*src << n) & BITMAP_LAST_WORD_MASK(nbits); else __bitmap_shift_left(dst, src, n, nbits); -- cgit v1.2.3 From cb78a0ce69fad2026825f957e24e2d9cda1ec9f1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Dec 2008 09:05:14 +1030 Subject: bitmap: fix seq_bitmap and seq_cpumask to take const pointer Impact: cleanup seq_bitmap just calls bitmap_scnprintf on the bits: that arg can be const. Similarly, seq_cpumask just calls seq_bitmap. Signed-off-by: Rusty Russell --- fs/seq_file.c | 3 ++- include/linux/seq_file.h | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/seq_file.c b/fs/seq_file.c index 16c211558c22..c99358a52176 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -462,7 +462,8 @@ int seq_dentry(struct seq_file *m, struct dentry *dentry, char *esc) return -1; } -int seq_bitmap(struct seq_file *m, unsigned long *bits, unsigned int nr_bits) +int seq_bitmap(struct seq_file *m, const unsigned long *bits, + unsigned int nr_bits) { if (m->count < m->size) { int len = bitmap_scnprintf(m->buf + m->count, diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index b3dfa72f13b9..952e0187ba16 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -50,8 +50,9 @@ int seq_path(struct seq_file *, struct path *, char *); int seq_dentry(struct seq_file *, struct dentry *, char *); int seq_path_root(struct seq_file *m, struct path *path, struct path *root, char *esc); -int seq_bitmap(struct seq_file *m, unsigned long *bits, unsigned int nr_bits); -static inline int seq_cpumask(struct seq_file *m, cpumask_t *mask) +int seq_bitmap(struct seq_file *m, const unsigned long *bits, + unsigned int nr_bits); +static inline int seq_cpumask(struct seq_file *m, const struct cpumask *mask) { return seq_bitmap(m, mask->bits, NR_CPUS); } -- cgit v1.2.3 From b3199c025d1646e25e7d1d640dd605db251dccf8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Dec 2008 09:05:14 +1030 Subject: cpumask: switch over to cpu_online/possible/active/present_mask: core Impact: cleanup This implements the obsolescent cpu_online_map in terms of cpu_online_mask, rather than the other way around. Same for the other maps. The documentation comments are also updated to refer to _mask rather than _map. Signed-off-by: Rusty Russell Signed-off-by: Mike Travis --- include/linux/cpumask.h | 75 +++++++++++++++++++------------------------------ kernel/cpu.c | 49 +++++++++++++++----------------- 2 files changed, 52 insertions(+), 72 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index b5ad19a6f43f..db2341beca45 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -416,65 +416,54 @@ int __next_cpu_nr(int n, const cpumask_t *srcp); /* * The following particular system cpumasks and operations manage - * possible, present, active and online cpus. Each of them is a fixed size - * bitmap of size NR_CPUS. + * possible, present, active and online cpus. * - * #ifdef CONFIG_HOTPLUG_CPU - * cpu_possible_map - has bit 'cpu' set iff cpu is populatable - * cpu_present_map - has bit 'cpu' set iff cpu is populated - * cpu_online_map - has bit 'cpu' set iff cpu available to scheduler - * cpu_active_map - has bit 'cpu' set iff cpu available to migration - * #else - * cpu_possible_map - has bit 'cpu' set iff cpu is populated - * cpu_present_map - copy of cpu_possible_map - * cpu_online_map - has bit 'cpu' set iff cpu available to scheduler - * #endif + * cpu_possible_mask- has bit 'cpu' set iff cpu is populatable + * cpu_present_mask - has bit 'cpu' set iff cpu is populated + * cpu_online_mask - has bit 'cpu' set iff cpu available to scheduler + * cpu_active_mask - has bit 'cpu' set iff cpu available to migration * - * In either case, NR_CPUS is fixed at compile time, as the static - * size of these bitmaps. The cpu_possible_map is fixed at boot - * time, as the set of CPU id's that it is possible might ever - * be plugged in at anytime during the life of that system boot. - * The cpu_present_map is dynamic(*), representing which CPUs - * are currently plugged in. And cpu_online_map is the dynamic - * subset of cpu_present_map, indicating those CPUs available - * for scheduling. + * If !CONFIG_HOTPLUG_CPU, present == possible, and active == online. * - * If HOTPLUG is enabled, then cpu_possible_map is forced to have + * The cpu_possible_mask is fixed at boot time, as the set of CPU id's + * that it is possible might ever be plugged in at anytime during the + * life of that system boot. The cpu_present_mask is dynamic(*), + * representing which CPUs are currently plugged in. And + * cpu_online_mask is the dynamic subset of cpu_present_mask, + * indicating those CPUs available for scheduling. + * + * If HOTPLUG is enabled, then cpu_possible_mask is forced to have * all NR_CPUS bits set, otherwise it is just the set of CPUs that * ACPI reports present at boot. * - * If HOTPLUG is enabled, then cpu_present_map varies dynamically, + * If HOTPLUG is enabled, then cpu_present_mask varies dynamically, * depending on what ACPI reports as currently plugged in, otherwise - * cpu_present_map is just a copy of cpu_possible_map. + * cpu_present_mask is just a copy of cpu_possible_mask. * - * (*) Well, cpu_present_map is dynamic in the hotplug case. If not - * hotplug, it's a copy of cpu_possible_map, hence fixed at boot. + * (*) Well, cpu_present_mask is dynamic in the hotplug case. If not + * hotplug, it's a copy of cpu_possible_mask, hence fixed at boot. * * Subtleties: * 1) UP arch's (NR_CPUS == 1, CONFIG_SMP not defined) hardcode * assumption that their single CPU is online. The UP - * cpu_{online,possible,present}_maps are placebos. Changing them + * cpu_{online,possible,present}_masks are placebos. Changing them * will have no useful affect on the following num_*_cpus() * and cpu_*() macros in the UP case. This ugliness is a UP * optimization - don't waste any instructions or memory references * asking if you're online or how many CPUs there are if there is * only one CPU. - * 2) Most SMP arch's #define some of these maps to be some - * other map specific to that arch. Therefore, the following - * must be #define macros, not inlines. To see why, examine - * the assembly code produced by the following. Note that - * set1() writes phys_x_map, but set2() writes x_map: - * int x_map, phys_x_map; - * #define set1(a) x_map = a - * inline void set2(int a) { x_map = a; } - * #define x_map phys_x_map - * main(){ set1(3); set2(5); } */ -extern cpumask_t cpu_possible_map; -extern cpumask_t cpu_online_map; -extern cpumask_t cpu_present_map; -extern cpumask_t cpu_active_map; +extern const struct cpumask *const cpu_possible_mask; +extern const struct cpumask *const cpu_online_mask; +extern const struct cpumask *const cpu_present_mask; +extern const struct cpumask *const cpu_active_mask; + +/* These strip const, as traditionally they weren't const. */ +#define cpu_possible_map (*(cpumask_t *)cpu_possible_mask) +#define cpu_online_map (*(cpumask_t *)cpu_online_mask) +#define cpu_present_map (*(cpumask_t *)cpu_present_mask) +#define cpu_active_map (*(cpumask_t *)cpu_active_mask) #if NR_CPUS > 1 #define num_online_cpus() cpus_weight_nr(cpu_online_map) @@ -1058,12 +1047,6 @@ static inline void free_bootmem_cpumask_var(cpumask_var_t mask) } #endif /* CONFIG_CPUMASK_OFFSTACK */ -/* The pointer versions of the maps, these will become the primary versions. */ -#define cpu_possible_mask ((const struct cpumask *)&cpu_possible_map) -#define cpu_online_mask ((const struct cpumask *)&cpu_online_map) -#define cpu_present_mask ((const struct cpumask *)&cpu_present_map) -#define cpu_active_mask ((const struct cpumask *)&cpu_active_map) - /* It's common to want to use cpu_all_mask in struct member initializers, * so it has to refer to an address rather than a pointer. */ extern const DECLARE_BITMAP(cpu_all_bits, NR_CPUS); diff --git a/kernel/cpu.c b/kernel/cpu.c index bae131a1211b..3ddc509b19c5 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -15,30 +15,8 @@ #include #include -/* - * Represents all cpu's present in the system - * In systems capable of hotplug, this map could dynamically grow - * as new cpu's are detected in the system via any platform specific - * method, such as ACPI for e.g. - */ -cpumask_t cpu_present_map __read_mostly; -EXPORT_SYMBOL(cpu_present_map); - -/* - * Represents all cpu's that are currently online. - */ -cpumask_t cpu_online_map __read_mostly; -EXPORT_SYMBOL(cpu_online_map); - -#ifdef CONFIG_INIT_ALL_POSSIBLE -cpumask_t cpu_possible_map __read_mostly = CPU_MASK_ALL; -#else -cpumask_t cpu_possible_map __read_mostly; -#endif -EXPORT_SYMBOL(cpu_possible_map); - #ifdef CONFIG_SMP -/* Serializes the updates to cpu_online_map, cpu_present_map */ +/* Serializes the updates to cpu_online_mask, cpu_present_mask */ static DEFINE_MUTEX(cpu_add_remove_lock); static __cpuinitdata RAW_NOTIFIER_HEAD(cpu_chain); @@ -65,8 +43,6 @@ void __init cpu_hotplug_init(void) cpu_hotplug.refcount = 0; } -cpumask_t cpu_active_map; - #ifdef CONFIG_HOTPLUG_CPU void get_online_cpus(void) @@ -97,7 +73,7 @@ EXPORT_SYMBOL_GPL(put_online_cpus); /* * The following two API's must be used when attempting - * to serialize the updates to cpu_online_map, cpu_present_map. + * to serialize the updates to cpu_online_mask, cpu_present_mask. */ void cpu_maps_update_begin(void) { @@ -503,3 +479,24 @@ EXPORT_SYMBOL_GPL(cpu_bit_bitmap); const DECLARE_BITMAP(cpu_all_bits, NR_CPUS) = CPU_BITS_ALL; EXPORT_SYMBOL(cpu_all_bits); + +#ifdef CONFIG_INIT_ALL_POSSIBLE +static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly + = CPU_BITS_ALL; +#else +static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly; +#endif +const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits); +EXPORT_SYMBOL(cpu_possible_mask); + +static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly; +const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits); +EXPORT_SYMBOL(cpu_online_mask); + +static DECLARE_BITMAP(cpu_present_bits, CONFIG_NR_CPUS) __read_mostly; +const struct cpumask *const cpu_present_mask = to_cpumask(cpu_present_bits); +EXPORT_SYMBOL(cpu_present_mask); + +static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly; +const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits); +EXPORT_SYMBOL(cpu_active_mask); -- cgit v1.2.3 From ae7a47e72e1a0b5e2b46d1596bc2c22942a73023 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Dec 2008 09:05:15 +1030 Subject: cpumask: make cpumask.h eat its own dogfood. Changes: 1) cpumask_t to struct cpumask, 2) cpus_weight_nr to cpumask_weight, 3) cpu_isset to cpumask_test_cpu, 4) ->bits to cpumask_bits() 5) cpu_*_map to cpu_*_mask. 6) for_each_cpu_mask_nr to for_each_cpu Signed-off-by: Rusty Russell --- include/linux/cpumask.h | 75 +++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index db2341beca45..e62a67156c53 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -268,6 +268,25 @@ static inline void __cpus_shift_left(cpumask_t *dstp, bitmap_shift_left(dstp->bits, srcp->bits, n, nbits); } +/** + * to_cpumask - convert an NR_CPUS bitmap to a struct cpumask * + * @bitmap: the bitmap + * + * There are a few places where cpumask_var_t isn't appropriate and + * static cpumasks must be used (eg. very early boot), yet we don't + * expose the definition of 'struct cpumask'. + * + * This does the conversion, and can be used as a constant initializer. + */ +#define to_cpumask(bitmap) \ + ((struct cpumask *)(1 ? (bitmap) \ + : (void *)sizeof(__check_is_bitmap(bitmap)))) + +static inline int __check_is_bitmap(const unsigned long *bitmap) +{ + return 1; +} + /* * Special-case data structure for "single bit set only" constant CPU masks. * @@ -278,11 +297,11 @@ static inline void __cpus_shift_left(cpumask_t *dstp, extern const unsigned long cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)]; -static inline const cpumask_t *get_cpu_mask(unsigned int cpu) +static inline const struct cpumask *get_cpu_mask(unsigned int cpu) { const unsigned long *p = cpu_bit_bitmap[1 + cpu % BITS_PER_LONG]; p -= cpu / BITS_PER_LONG; - return (const cpumask_t *)p; + return to_cpumask(p); } /* @@ -466,13 +485,13 @@ extern const struct cpumask *const cpu_active_mask; #define cpu_active_map (*(cpumask_t *)cpu_active_mask) #if NR_CPUS > 1 -#define num_online_cpus() cpus_weight_nr(cpu_online_map) -#define num_possible_cpus() cpus_weight_nr(cpu_possible_map) -#define num_present_cpus() cpus_weight_nr(cpu_present_map) -#define cpu_online(cpu) cpu_isset((cpu), cpu_online_map) -#define cpu_possible(cpu) cpu_isset((cpu), cpu_possible_map) -#define cpu_present(cpu) cpu_isset((cpu), cpu_present_map) -#define cpu_active(cpu) cpu_isset((cpu), cpu_active_map) +#define num_online_cpus() cpumask_weight(cpu_online_mask) +#define num_possible_cpus() cpumask_weight(cpu_possible_mask) +#define num_present_cpus() cpumask_weight(cpu_present_mask) +#define cpu_online(cpu) cpumask_test_cpu((cpu), cpu_online_mask) +#define cpu_possible(cpu) cpumask_test_cpu((cpu), cpu_possible_mask) +#define cpu_present(cpu) cpumask_test_cpu((cpu), cpu_present_mask) +#define cpu_active(cpu) cpumask_test_cpu((cpu), cpu_active_mask) #else #define num_online_cpus() 1 #define num_possible_cpus() 1 @@ -485,10 +504,6 @@ extern const struct cpumask *const cpu_active_mask; #define cpu_is_offline(cpu) unlikely(!cpu_online(cpu)) -#define for_each_possible_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_possible_map) -#define for_each_online_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_online_map) -#define for_each_present_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_present_map) - /* These are the new versions of the cpumask operators: passed by pointer. * The older versions will be implemented in terms of these, then deleted. */ #define cpumask_bits(maskp) ((maskp)->bits) @@ -676,7 +691,7 @@ static inline void cpumask_clear_cpu(int cpu, struct cpumask *dstp) * No static inline type checking - see Subtlety (1) above. */ #define cpumask_test_cpu(cpu, cpumask) \ - test_bit(cpumask_check(cpu), (cpumask)->bits) + test_bit(cpumask_check(cpu), cpumask_bits((cpumask))) /** * cpumask_test_and_set_cpu - atomically test and set a cpu in a cpumask @@ -919,7 +934,7 @@ static inline void cpumask_copy(struct cpumask *dstp, static inline int cpumask_scnprintf(char *buf, int len, const struct cpumask *srcp) { - return bitmap_scnprintf(buf, len, srcp->bits, nr_cpumask_bits); + return bitmap_scnprintf(buf, len, cpumask_bits(srcp), nr_cpumask_bits); } /** @@ -933,7 +948,7 @@ static inline int cpumask_scnprintf(char *buf, int len, static inline int cpumask_parse_user(const char __user *buf, int len, struct cpumask *dstp) { - return bitmap_parse_user(buf, len, dstp->bits, nr_cpumask_bits); + return bitmap_parse_user(buf, len, cpumask_bits(dstp), nr_cpumask_bits); } /** @@ -948,7 +963,8 @@ static inline int cpumask_parse_user(const char __user *buf, int len, static inline int cpulist_scnprintf(char *buf, int len, const struct cpumask *srcp) { - return bitmap_scnlistprintf(buf, len, srcp->bits, nr_cpumask_bits); + return bitmap_scnlistprintf(buf, len, cpumask_bits(srcp), + nr_cpumask_bits); } /** @@ -961,26 +977,7 @@ static inline int cpulist_scnprintf(char *buf, int len, */ static inline int cpulist_parse(const char *buf, struct cpumask *dstp) { - return bitmap_parselist(buf, dstp->bits, nr_cpumask_bits); -} - -/** - * to_cpumask - convert an NR_CPUS bitmap to a struct cpumask * - * @bitmap: the bitmap - * - * There are a few places where cpumask_var_t isn't appropriate and - * static cpumasks must be used (eg. very early boot), yet we don't - * expose the definition of 'struct cpumask'. - * - * This does the conversion, and can be used as a constant initializer. - */ -#define to_cpumask(bitmap) \ - ((struct cpumask *)(1 ? (bitmap) \ - : (void *)sizeof(__check_is_bitmap(bitmap)))) - -static inline int __check_is_bitmap(const unsigned long *bitmap) -{ - return 1; + return bitmap_parselist(buf, cpumask_bits(dstp), nr_cpumask_bits); } /** @@ -1055,6 +1052,10 @@ extern const DECLARE_BITMAP(cpu_all_bits, NR_CPUS); /* First bits of cpu_bit_bitmap are in fact unset. */ #define cpu_none_mask to_cpumask(cpu_bit_bitmap[0]) +#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask) +#define for_each_online_cpu(cpu) for_each_cpu((cpu), cpu_online_mask) +#define for_each_present_cpu(cpu) for_each_cpu((cpu), cpu_present_mask) + /* Wrappers for arch boot code to manipulate normally-constant masks */ static inline void set_cpu_possible(unsigned int cpu, bool possible) { -- cgit v1.2.3 From 3fa41520696fec2815e2d88fbcccdda77ba4d693 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Dec 2008 09:05:16 +1030 Subject: cpumask: make set_cpu_*/init_cpu_* out-of-line They're only for use in boot/cpu hotplug code anyway, and this avoids the use of deprecated cpu_*_map. Stephen Rothwell points out that gcc 4.2.4 (on powerpc at least) didn't like the cast away of const anyway: include/linux/cpumask.h: In function 'set_cpu_possible': include/linux/cpumask.h:1052: warning: passing argument 2 of 'cpumask_set_cpu' discards qualifiers from pointer target type So this kills two birds with one stone. Signed-off-by: Rusty Russell --- include/linux/cpumask.h | 53 +++++++------------------------------------------ kernel/cpu.c | 47 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index e62a67156c53..7c178a6baae3 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -1057,50 +1057,11 @@ extern const DECLARE_BITMAP(cpu_all_bits, NR_CPUS); #define for_each_present_cpu(cpu) for_each_cpu((cpu), cpu_present_mask) /* Wrappers for arch boot code to manipulate normally-constant masks */ -static inline void set_cpu_possible(unsigned int cpu, bool possible) -{ - if (possible) - cpumask_set_cpu(cpu, &cpu_possible_map); - else - cpumask_clear_cpu(cpu, &cpu_possible_map); -} - -static inline void set_cpu_present(unsigned int cpu, bool present) -{ - if (present) - cpumask_set_cpu(cpu, &cpu_present_map); - else - cpumask_clear_cpu(cpu, &cpu_present_map); -} - -static inline void set_cpu_online(unsigned int cpu, bool online) -{ - if (online) - cpumask_set_cpu(cpu, &cpu_online_map); - else - cpumask_clear_cpu(cpu, &cpu_online_map); -} - -static inline void set_cpu_active(unsigned int cpu, bool active) -{ - if (active) - cpumask_set_cpu(cpu, &cpu_active_map); - else - cpumask_clear_cpu(cpu, &cpu_active_map); -} - -static inline void init_cpu_present(const struct cpumask *src) -{ - cpumask_copy(&cpu_present_map, src); -} - -static inline void init_cpu_possible(const struct cpumask *src) -{ - cpumask_copy(&cpu_possible_map, src); -} - -static inline void init_cpu_online(const struct cpumask *src) -{ - cpumask_copy(&cpu_online_map, src); -} +void set_cpu_possible(unsigned int cpu, bool possible); +void set_cpu_present(unsigned int cpu, bool present); +void set_cpu_online(unsigned int cpu, bool online); +void set_cpu_active(unsigned int cpu, bool active); +void init_cpu_present(const struct cpumask *src); +void init_cpu_possible(const struct cpumask *src); +void init_cpu_online(const struct cpumask *src); #endif /* __LINUX_CPUMASK_H */ diff --git a/kernel/cpu.c b/kernel/cpu.c index 3ddc509b19c5..2c9f78f3a2fc 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -500,3 +500,50 @@ EXPORT_SYMBOL(cpu_present_mask); static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly; const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits); EXPORT_SYMBOL(cpu_active_mask); + +void set_cpu_possible(unsigned int cpu, bool possible) +{ + if (possible) + cpumask_set_cpu(cpu, to_cpumask(cpu_possible_bits)); + else + cpumask_clear_cpu(cpu, to_cpumask(cpu_possible_bits)); +} + +void set_cpu_present(unsigned int cpu, bool present) +{ + if (present) + cpumask_set_cpu(cpu, to_cpumask(cpu_present_bits)); + else + cpumask_clear_cpu(cpu, to_cpumask(cpu_present_bits)); +} + +void set_cpu_online(unsigned int cpu, bool online) +{ + if (online) + cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits)); + else + cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits)); +} + +void set_cpu_active(unsigned int cpu, bool active) +{ + if (active) + cpumask_set_cpu(cpu, to_cpumask(cpu_active_bits)); + else + cpumask_clear_cpu(cpu, to_cpumask(cpu_active_bits)); +} + +void init_cpu_present(const struct cpumask *src) +{ + cpumask_copy(to_cpumask(cpu_present_bits), src); +} + +void init_cpu_possible(const struct cpumask *src) +{ + cpumask_copy(to_cpumask(cpu_possible_bits), src); +} + +void init_cpu_online(const struct cpumask *src) +{ + cpumask_copy(to_cpumask(cpu_online_bits), src); +} -- cgit v1.2.3 From 54b11e6d57a10aa9d0009efd93873e17bffd5d30 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Dec 2008 09:05:16 +1030 Subject: cpumask: smp_call_function_many() Impact: Implementation change to remove cpumask_t from stack. Actually change smp_call_function_mask() to smp_call_function_many(). We avoid cpumasks on the stack in this version. (S390 has its own version, but that's going away apparently). We have to do some dancing to figure out if 0 or 1 other cpus are in the mask supplied and the online mask without allocating a tmp cpumask. It's still fairly cheap. We allocate the cpumask at the end of the call_function_data structure: if allocation fails we fallback to smp_call_function_single rather than using the baroque quiescing code (which needs a cpumask on stack). (Thanks to Hiroshi Shimamoto for spotting several bugs in previous versions!) Signed-off-by: Rusty Russell Signed-off-by: Mike Travis Cc: Hiroshi Shimamoto Cc: npiggin@suse.de Cc: axboe@kernel.dk --- include/linux/smp.h | 15 +++--- kernel/smp.c | 139 ++++++++++++++++++---------------------------------- 2 files changed, 57 insertions(+), 97 deletions(-) (limited to 'include/linux') diff --git a/include/linux/smp.h b/include/linux/smp.h index 2f85f3b04bc4..b82466968101 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -67,15 +67,16 @@ extern void smp_cpus_done(unsigned int max_cpus); * Call a function on all other processors */ int smp_call_function(void(*func)(void *info), void *info, int wait); -/* Deprecated: use smp_call_function_many() which uses a cpumask ptr. */ -int smp_call_function_mask(cpumask_t mask, void(*func)(void *info), void *info, - int wait); +void smp_call_function_many(const struct cpumask *mask, + void (*func)(void *info), void *info, bool wait); -static inline void smp_call_function_many(const struct cpumask *mask, - void (*func)(void *info), void *info, - int wait) +/* Deprecated: Use smp_call_function_many which takes a pointer to the mask. */ +static inline int +smp_call_function_mask(cpumask_t mask, void(*func)(void *info), void *info, + int wait) { - smp_call_function_mask(*mask, func, info, wait); + smp_call_function_many(&mask, func, info, wait); + return 0; } int smp_call_function_single(int cpuid, void (*func) (void *info), void *info, diff --git a/kernel/smp.c b/kernel/smp.c index 75c8dde58c55..9f0eafed1399 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -24,8 +24,8 @@ struct call_function_data { struct call_single_data csd; spinlock_t lock; unsigned int refs; - cpumask_t cpumask; struct rcu_head rcu_head; + unsigned long cpumask_bits[]; }; struct call_single_queue { @@ -110,13 +110,13 @@ void generic_smp_call_function_interrupt(void) list_for_each_entry_rcu(data, &call_function_queue, csd.list) { int refs; - if (!cpu_isset(cpu, data->cpumask)) + if (!cpumask_test_cpu(cpu, to_cpumask(data->cpumask_bits))) continue; data->csd.func(data->csd.info); spin_lock(&data->lock); - cpu_clear(cpu, data->cpumask); + cpumask_clear_cpu(cpu, to_cpumask(data->cpumask_bits)); WARN_ON(data->refs == 0); data->refs--; refs = data->refs; @@ -266,51 +266,13 @@ void __smp_call_function_single(int cpu, struct call_single_data *data) generic_exec_single(cpu, data); } -/* Dummy function */ -static void quiesce_dummy(void *unused) -{ -} - -/* - * Ensure stack based data used in call function mask is safe to free. - * - * This is needed by smp_call_function_mask when using on-stack data, because - * a single call function queue is shared by all CPUs, and any CPU may pick up - * the data item on the queue at any time before it is deleted. So we need to - * ensure that all CPUs have transitioned through a quiescent state after - * this call. - * - * This is a very slow function, implemented by sending synchronous IPIs to - * all possible CPUs. For this reason, we have to alloc data rather than use - * stack based data even in the case of synchronous calls. The stack based - * data is then just used for deadlock/oom fallback which will be very rare. - * - * If a faster scheme can be made, we could go back to preferring stack based - * data -- the data allocation/free is non-zero cost. - */ -static void smp_call_function_mask_quiesce_stack(cpumask_t mask) -{ - struct call_single_data data; - int cpu; - - data.func = quiesce_dummy; - data.info = NULL; - - for_each_cpu_mask(cpu, mask) { - data.flags = CSD_FLAG_WAIT; - generic_exec_single(cpu, &data); - } -} - /** - * smp_call_function_mask(): Run a function on a set of other CPUs. - * @mask: The set of cpus to run on. + * smp_call_function_many(): Run a function on a set of other CPUs. + * @mask: The set of cpus to run on (only runs on online subset). * @func: The function to run. This must be fast and non-blocking. * @info: An arbitrary pointer to pass to the function. * @wait: If true, wait (atomically) until function has completed on other CPUs. * - * Returns 0 on success, else a negative status code. - * * If @wait is true, then returns once @func has returned. Note that @wait * will be implicitly turned on in case of allocation failures, since * we fall back to on-stack allocation. @@ -319,53 +281,57 @@ static void smp_call_function_mask_quiesce_stack(cpumask_t mask) * hardware interrupt handler or from a bottom half handler. Preemption * must be disabled when calling this function. */ -int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info, - int wait) +void smp_call_function_many(const struct cpumask *mask, + void (*func)(void *), void *info, + bool wait) { - struct call_function_data d; - struct call_function_data *data = NULL; - cpumask_t allbutself; + struct call_function_data *data; unsigned long flags; - int cpu, num_cpus; - int slowpath = 0; + int cpu, next_cpu; /* Can deadlock when called with interrupts disabled */ WARN_ON(irqs_disabled()); - cpu = smp_processor_id(); - allbutself = cpu_online_map; - cpu_clear(cpu, allbutself); - cpus_and(mask, mask, allbutself); - num_cpus = cpus_weight(mask); - - /* - * If zero CPUs, return. If just a single CPU, turn this request - * into a targetted single call instead since it's faster. - */ - if (!num_cpus) - return 0; - else if (num_cpus == 1) { - cpu = first_cpu(mask); - return smp_call_function_single(cpu, func, info, wait); + /* So, what's a CPU they want? Ignoring this one. */ + cpu = cpumask_first_and(mask, cpu_online_mask); + if (cpu == smp_processor_id()) + cpu = cpumask_next_and(cpu, mask, cpu_online_mask); + /* No online cpus? We're done. */ + if (cpu >= nr_cpu_ids) + return; + + /* Do we have another CPU which isn't us? */ + next_cpu = cpumask_next_and(cpu, mask, cpu_online_mask); + if (next_cpu == smp_processor_id()) + next_cpu = cpumask_next_and(next_cpu, mask, cpu_online_mask); + + /* Fastpath: do that cpu by itself. */ + if (next_cpu >= nr_cpu_ids) { + smp_call_function_single(cpu, func, info, wait); + return; } - data = kmalloc(sizeof(*data), GFP_ATOMIC); - if (data) { - data->csd.flags = CSD_FLAG_ALLOC; - if (wait) - data->csd.flags |= CSD_FLAG_WAIT; - } else { - data = &d; - data->csd.flags = CSD_FLAG_WAIT; - wait = 1; - slowpath = 1; + data = kmalloc(sizeof(*data) + cpumask_size(), GFP_ATOMIC); + if (unlikely(!data)) { + /* Slow path. */ + for_each_online_cpu(cpu) { + if (cpu == smp_processor_id()) + continue; + if (cpumask_test_cpu(cpu, mask)) + smp_call_function_single(cpu, func, info, wait); + } + return; } spin_lock_init(&data->lock); + data->csd.flags = CSD_FLAG_ALLOC; + if (wait) + data->csd.flags |= CSD_FLAG_WAIT; data->csd.func = func; data->csd.info = info; - data->refs = num_cpus; - data->cpumask = mask; + cpumask_and(to_cpumask(data->cpumask_bits), mask, cpu_online_mask); + cpumask_clear_cpu(smp_processor_id(), to_cpumask(data->cpumask_bits)); + data->refs = cpumask_weight(to_cpumask(data->cpumask_bits)); spin_lock_irqsave(&call_function_lock, flags); list_add_tail_rcu(&data->csd.list, &call_function_queue); @@ -377,18 +343,13 @@ int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info, smp_mb(); /* Send a message to all CPUs in the map */ - arch_send_call_function_ipi(mask); + arch_send_call_function_ipi(*to_cpumask(data->cpumask_bits)); /* optionally wait for the CPUs to complete */ - if (wait) { + if (wait) csd_flag_wait(&data->csd); - if (unlikely(slowpath)) - smp_call_function_mask_quiesce_stack(mask); - } - - return 0; } -EXPORT_SYMBOL(smp_call_function_mask); +EXPORT_SYMBOL(smp_call_function_many); /** * smp_call_function(): Run a function on all other CPUs. @@ -396,7 +357,7 @@ EXPORT_SYMBOL(smp_call_function_mask); * @info: An arbitrary pointer to pass to the function. * @wait: If true, wait (atomically) until function has completed on other CPUs. * - * Returns 0 on success, else a negative status code. + * Returns 0. * * If @wait is true, then returns once @func has returned; otherwise * it returns just before the target cpu calls @func. In case of allocation @@ -407,12 +368,10 @@ EXPORT_SYMBOL(smp_call_function_mask); */ int smp_call_function(void (*func)(void *), void *info, int wait) { - int ret; - preempt_disable(); - ret = smp_call_function_mask(cpu_online_map, func, info, wait); + smp_call_function_many(cpu_online_mask, func, info, wait); preempt_enable(); - return ret; + return 0; } EXPORT_SYMBOL(smp_call_function); -- cgit v1.2.3 From e12f0102ac81d660c9f801d0a0e10ccf4537a9de Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Dec 2008 09:05:19 +1030 Subject: cpumask: Use nr_cpu_ids in seq_cpumask Impact: cleanup, futureproof nr_cpu_ids is the (badly named) runtime limit on possible CPU numbers; ie. the variable version of NR_CPUS. With the new cpumask operators, only bits less than this are defined. So we should use it everywhere, rather than NR_CPUS. Eventually this will make it possible to allocate cpumasks of the minimal length at runtime. Signed-off-by: Rusty Russell Signed-off-by: Mike Travis Acked-by: Ingo Molnar --- include/linux/seq_file.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 952e0187ba16..40ea5058c2ec 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -54,7 +54,7 @@ int seq_bitmap(struct seq_file *m, const unsigned long *bits, unsigned int nr_bits); static inline int seq_cpumask(struct seq_file *m, const struct cpumask *mask) { - return seq_bitmap(m, mask->bits, NR_CPUS); + return seq_bitmap(m, mask->bits, nr_cpu_ids); } static inline int seq_nodemask(struct seq_file *m, nodemask_t *mask) -- cgit v1.2.3 From 457533a7d3402d1d91fbc125c8bd1bd16dcd3cd4 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Wed, 31 Dec 2008 15:11:37 +0100 Subject: [PATCH] fix scaled & unscaled cputime accounting The utimescaled / stimescaled fields in the task structure and the global cpustat should be set on all architectures. On s390 the calls to account_user_time_scaled and account_system_time_scaled never have been added. In addition system time that is accounted as guest time to the user time of a process is accounted to the scaled system time instead of the scaled user time. To fix the bugs and to prevent future forgetfulness this patch merges account_system_time_scaled into account_system_time and account_user_time_scaled into account_user_time. Cc: Benjamin Herrenschmidt Cc: Hidetoshi Seto Cc: Tony Luck Cc: Jeremy Fitzhardinge Cc: Chris Wright Cc: Michael Neuling Acked-by: Paul Mackerras Signed-off-by: Martin Schwidefsky --- arch/ia64/kernel/time.c | 12 ++++-------- arch/powerpc/kernel/time.c | 7 ++----- arch/s390/kernel/vtime.c | 10 +++++----- include/linux/kernel_stat.h | 6 ++---- kernel/sched.c | 41 ++++++++++++++++------------------------- kernel/time/tick-sched.c | 5 +++-- kernel/timer.c | 12 +++++------- 7 files changed, 37 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index 65c10a42c88f..4ee367817049 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -93,13 +93,11 @@ void ia64_account_on_switch(struct task_struct *prev, struct task_struct *next) now = ia64_get_itc(); delta_stime = cycle_to_cputime(pi->ac_stime + (now - pi->ac_stamp)); - account_system_time(prev, 0, delta_stime); - account_system_time_scaled(prev, delta_stime); + account_system_time(prev, 0, delta_stime, delta_stime); if (pi->ac_utime) { delta_utime = cycle_to_cputime(pi->ac_utime); - account_user_time(prev, delta_utime); - account_user_time_scaled(prev, delta_utime); + account_user_time(prev, delta_utime, delta_utime); } pi->ac_stamp = ni->ac_stamp = now; @@ -122,8 +120,7 @@ void account_system_vtime(struct task_struct *tsk) now = ia64_get_itc(); delta_stime = cycle_to_cputime(ti->ac_stime + (now - ti->ac_stamp)); - account_system_time(tsk, 0, delta_stime); - account_system_time_scaled(tsk, delta_stime); + account_system_time(tsk, 0, delta_stime, delta_stime); ti->ac_stime = 0; ti->ac_stamp = now; @@ -143,8 +140,7 @@ void account_process_tick(struct task_struct *p, int user_tick) if (ti->ac_utime) { delta_utime = cycle_to_cputime(ti->ac_utime); - account_user_time(p, delta_utime); - account_user_time_scaled(p, delta_utime); + account_user_time(p, delta_utime, delta_utime); ti->ac_utime = 0; } } diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index e1f3a5140429..92650ccad2e1 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -256,8 +256,7 @@ void account_system_vtime(struct task_struct *tsk) delta += sys_time; get_paca()->system_time = 0; } - account_system_time(tsk, 0, delta); - account_system_time_scaled(tsk, deltascaled); + account_system_time(tsk, 0, delta, deltascaled); per_cpu(cputime_last_delta, smp_processor_id()) = delta; per_cpu(cputime_scaled_last_delta, smp_processor_id()) = deltascaled; local_irq_restore(flags); @@ -275,10 +274,8 @@ void account_process_tick(struct task_struct *tsk, int user_tick) utime = get_paca()->user_time; get_paca()->user_time = 0; - account_user_time(tsk, utime); - utimescaled = cputime_to_scaled(utime); - account_user_time_scaled(tsk, utimescaled); + account_user_time(tsk, utime, utimescaled); } /* diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 75a6e62ea973..07283aea2e56 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -50,12 +50,12 @@ void account_process_tick(struct task_struct *tsk, int user_tick) rcu_user_flag = cputime != 0; S390_lowcore.user_timer -= cputime << 12; S390_lowcore.steal_clock -= cputime << 12; - account_user_time(tsk, cputime); + account_user_time(tsk, cputime, cputime); cputime = S390_lowcore.system_timer >> 12; S390_lowcore.system_timer -= cputime << 12; S390_lowcore.steal_clock -= cputime << 12; - account_system_time(tsk, HARDIRQ_OFFSET, cputime); + account_system_time(tsk, HARDIRQ_OFFSET, cputime, cputime); cputime = S390_lowcore.steal_clock; if ((__s64) cputime > 0) { @@ -82,12 +82,12 @@ void account_vtime(struct task_struct *tsk) cputime = S390_lowcore.user_timer >> 12; S390_lowcore.user_timer -= cputime << 12; S390_lowcore.steal_clock -= cputime << 12; - account_user_time(tsk, cputime); + account_user_time(tsk, cputime, cputime); cputime = S390_lowcore.system_timer >> 12; S390_lowcore.system_timer -= cputime << 12; S390_lowcore.steal_clock -= cputime << 12; - account_system_time(tsk, 0, cputime); + account_system_time(tsk, 0, cputime, cputime); } /* @@ -107,7 +107,7 @@ void account_system_vtime(struct task_struct *tsk) cputime = S390_lowcore.system_timer >> 12; S390_lowcore.system_timer -= cputime << 12; S390_lowcore.steal_clock -= cputime << 12; - account_system_time(tsk, 0, cputime); + account_system_time(tsk, 0, cputime, cputime); } EXPORT_SYMBOL_GPL(account_system_vtime); diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index 4ee4b3d2316f..c78a459662a6 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h @@ -79,10 +79,8 @@ static inline unsigned int kstat_irqs(unsigned int irq) } extern unsigned long long task_delta_exec(struct task_struct *); -extern void account_user_time(struct task_struct *, cputime_t); -extern void account_user_time_scaled(struct task_struct *, cputime_t); -extern void account_system_time(struct task_struct *, int, cputime_t); -extern void account_system_time_scaled(struct task_struct *, cputime_t); +extern void account_user_time(struct task_struct *, cputime_t, cputime_t); +extern void account_system_time(struct task_struct *, int, cputime_t, cputime_t); extern void account_steal_time(struct task_struct *, cputime_t); #endif /* _LINUX_KERNEL_STAT_H */ diff --git a/kernel/sched.c b/kernel/sched.c index fff1c4a20b65..5b03679ff712 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4080,13 +4080,17 @@ unsigned long long task_delta_exec(struct task_struct *p) * Account user cpu time to a process. * @p: the process that the cpu time gets accounted to * @cputime: the cpu time spent in user space since the last update + * @cputime_scaled: cputime scaled by cpu frequency */ -void account_user_time(struct task_struct *p, cputime_t cputime) +void account_user_time(struct task_struct *p, cputime_t cputime, + cputime_t cputime_scaled) { struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; cputime64_t tmp; + /* Add user time to process. */ p->utime = cputime_add(p->utime, cputime); + p->utimescaled = cputime_add(p->utimescaled, cputime_scaled); account_group_user_time(p, cputime); /* Add user time to cpustat. */ @@ -4103,51 +4107,49 @@ void account_user_time(struct task_struct *p, cputime_t cputime) * Account guest cpu time to a process. * @p: the process that the cpu time gets accounted to * @cputime: the cpu time spent in virtual machine since the last update + * @cputime_scaled: cputime scaled by cpu frequency */ -static void account_guest_time(struct task_struct *p, cputime_t cputime) +static void account_guest_time(struct task_struct *p, cputime_t cputime, + cputime_t cputime_scaled) { cputime64_t tmp; struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; tmp = cputime_to_cputime64(cputime); + /* Add guest time to process. */ p->utime = cputime_add(p->utime, cputime); + p->utimescaled = cputime_add(p->utimescaled, cputime_scaled); account_group_user_time(p, cputime); p->gtime = cputime_add(p->gtime, cputime); + /* Add guest time to cpustat. */ cpustat->user = cputime64_add(cpustat->user, tmp); cpustat->guest = cputime64_add(cpustat->guest, tmp); } -/* - * Account scaled user cpu time to a process. - * @p: the process that the cpu time gets accounted to - * @cputime: the cpu time spent in user space since the last update - */ -void account_user_time_scaled(struct task_struct *p, cputime_t cputime) -{ - p->utimescaled = cputime_add(p->utimescaled, cputime); -} - /* * Account system cpu time to a process. * @p: the process that the cpu time gets accounted to * @hardirq_offset: the offset to subtract from hardirq_count() * @cputime: the cpu time spent in kernel space since the last update + * @cputime_scaled: cputime scaled by cpu frequency */ void account_system_time(struct task_struct *p, int hardirq_offset, - cputime_t cputime) + cputime_t cputime, cputime_t cputime_scaled) { struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; struct rq *rq = this_rq(); cputime64_t tmp; if ((p->flags & PF_VCPU) && (irq_count() - hardirq_offset == 0)) { - account_guest_time(p, cputime); + account_guest_time(p, cputime, cputime_scaled); return; } + /* Add system time to process. */ p->stime = cputime_add(p->stime, cputime); + p->stimescaled = cputime_add(p->stimescaled, cputime_scaled); account_group_system_time(p, cputime); /* Add system time to cpustat. */ @@ -4166,17 +4168,6 @@ void account_system_time(struct task_struct *p, int hardirq_offset, acct_update_integrals(p); } -/* - * Account scaled system cpu time to a process. - * @p: the process that the cpu time gets accounted to - * @hardirq_offset: the offset to subtract from hardirq_count() - * @cputime: the cpu time spent in kernel space since the last update - */ -void account_system_time_scaled(struct task_struct *p, cputime_t cputime) -{ - p->stimescaled = cputime_add(p->stimescaled, cputime); -} - /* * Account for involuntary wait time. * @p: the process from which the cpu time has been stolen diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 8f3fc2582d38..1f2fce2479fe 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -420,6 +420,7 @@ void tick_nohz_restart_sched_tick(void) int cpu = smp_processor_id(); struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); unsigned long ticks; + cputime_t cputime; ktime_t now; local_irq_disable(); @@ -452,8 +453,8 @@ void tick_nohz_restart_sched_tick(void) */ if (ticks && ticks < LONG_MAX) { add_preempt_count(HARDIRQ_OFFSET); - account_system_time(current, HARDIRQ_OFFSET, - jiffies_to_cputime(ticks)); + cputime = jiffies_to_cputime(ticks); + account_system_time(current, HARDIRQ_OFFSET, cputime, cputime); sub_preempt_count(HARDIRQ_OFFSET); } diff --git a/kernel/timer.c b/kernel/timer.c index 566257d1dc10..b5efb528aa1d 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1023,13 +1023,11 @@ void account_process_tick(struct task_struct *p, int user_tick) { cputime_t one_jiffy = jiffies_to_cputime(1); - if (user_tick) { - account_user_time(p, one_jiffy); - account_user_time_scaled(p, cputime_to_scaled(one_jiffy)); - } else { - account_system_time(p, HARDIRQ_OFFSET, one_jiffy); - account_system_time_scaled(p, cputime_to_scaled(one_jiffy)); - } + if (user_tick) + account_user_time(p, one_jiffy, cputime_to_scaled(one_jiffy)); + else + account_system_time(p, HARDIRQ_OFFSET, one_jiffy, + cputime_to_scaled(one_jiffy)); } #endif -- cgit v1.2.3 From 79741dd35713ff4f6fd0eafd59fa94e8a4ba922d Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Wed, 31 Dec 2008 15:11:38 +0100 Subject: [PATCH] idle cputime accounting The cpu time spent by the idle process actually doing something is currently accounted as idle time. This is plain wrong, the architectures that support VIRT_CPU_ACCOUNTING=y can do better: distinguish between the time spent doing nothing and the time spent by idle doing work. The first is accounted with account_idle_time and the second with account_system_time. The architectures that use the account_xxx_time interface directly and not the account_xxx_ticks interface now need to do the check for the idle process in their arch code. In particular to improve the system vs true idle time accounting the arch code needs to measure the true idle time instead of just testing for the idle process. To improve the tick based accounting as well we would need an architecture primitive that can tell us if the pt_regs of the interrupted context points to the magic instruction that halts the cpu. In addition idle time is no more added to the stime of the idle process. This field now contains the system time of the idle process as it should be. On systems without VIRT_CPU_ACCOUNTING this will always be zero as every tick that occurs while idle is running will be accounted as idle time. This patch contains the necessary common code changes to be able to distinguish idle system time and true idle time. The architectures with support for VIRT_CPU_ACCOUNTING need some changes to exploit this. Signed-off-by: Martin Schwidefsky --- arch/ia64/kernel/time.c | 10 ++++-- arch/powerpc/kernel/process.c | 1 + arch/powerpc/kernel/time.c | 13 +++++-- arch/s390/kernel/vtime.c | 20 ++++++++--- arch/x86/xen/time.c | 10 +++--- include/linux/kernel_stat.h | 7 +++- include/linux/sched.h | 1 - kernel/sched.c | 80 ++++++++++++++++++++++++++++++++++--------- kernel/time/tick-sched.c | 13 ++++--- kernel/timer.c | 13 ------- 10 files changed, 114 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index 4ee367817049..f0ebb342409d 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -93,7 +93,10 @@ void ia64_account_on_switch(struct task_struct *prev, struct task_struct *next) now = ia64_get_itc(); delta_stime = cycle_to_cputime(pi->ac_stime + (now - pi->ac_stamp)); - account_system_time(prev, 0, delta_stime, delta_stime); + if (idle_task(smp_processor_id()) != prev) + account_system_time(prev, 0, delta_stime, delta_stime); + else + account_idle_time(delta_stime); if (pi->ac_utime) { delta_utime = cycle_to_cputime(pi->ac_utime); @@ -120,7 +123,10 @@ void account_system_vtime(struct task_struct *tsk) now = ia64_get_itc(); delta_stime = cycle_to_cputime(ti->ac_stime + (now - ti->ac_stamp)); - account_system_time(tsk, 0, delta_stime, delta_stime); + if (irq_count() || idle_task(smp_processor_id()) != tsk) + account_system_time(tsk, 0, delta_stime, delta_stime); + else + account_idle_time(delta_stime); ti->ac_stime = 0; ti->ac_stamp = now; diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 51b201ddf9a1..fb7049c054c0 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 92650ccad2e1..3be355c1cfa7 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -256,7 +256,10 @@ void account_system_vtime(struct task_struct *tsk) delta += sys_time; get_paca()->system_time = 0; } - account_system_time(tsk, 0, delta, deltascaled); + if (in_irq() || idle_task(smp_processor_id()) != tsk) + account_system_time(tsk, 0, delta, deltascaled); + else + account_idle_time(delta); per_cpu(cputime_last_delta, smp_processor_id()) = delta; per_cpu(cputime_scaled_last_delta, smp_processor_id()) = deltascaled; local_irq_restore(flags); @@ -335,8 +338,12 @@ void calculate_steal_time(void) tb = mftb(); purr = mfspr(SPRN_PURR); stolen = (tb - pme->tb) - (purr - pme->purr); - if (stolen > 0) - account_steal_time(current, stolen); + if (stolen > 0) { + if (idle_task(smp_processor_id()) != current) + account_steal_time(stolen); + else + account_idle_time(stolen); + } pme->tb = tb; pme->purr = purr; } diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 07283aea2e56..4a4a34caec55 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -55,13 +55,19 @@ void account_process_tick(struct task_struct *tsk, int user_tick) cputime = S390_lowcore.system_timer >> 12; S390_lowcore.system_timer -= cputime << 12; S390_lowcore.steal_clock -= cputime << 12; - account_system_time(tsk, HARDIRQ_OFFSET, cputime, cputime); + if (idle_task(smp_processor_id()) != current) + account_system_time(tsk, HARDIRQ_OFFSET, cputime, cputime); + else + account_idle_time(cputime); cputime = S390_lowcore.steal_clock; if ((__s64) cputime > 0) { cputime >>= 12; S390_lowcore.steal_clock -= cputime << 12; - account_steal_time(tsk, cputime); + if (idle_task(smp_processor_id()) != current) + account_steal_time(cputime); + else + account_idle_time(cputime); } } @@ -87,7 +93,10 @@ void account_vtime(struct task_struct *tsk) cputime = S390_lowcore.system_timer >> 12; S390_lowcore.system_timer -= cputime << 12; S390_lowcore.steal_clock -= cputime << 12; - account_system_time(tsk, 0, cputime, cputime); + if (idle_task(smp_processor_id()) != current) + account_system_time(tsk, 0, cputime, cputime); + else + account_idle_time(cputime); } /* @@ -107,7 +116,10 @@ void account_system_vtime(struct task_struct *tsk) cputime = S390_lowcore.system_timer >> 12; S390_lowcore.system_timer -= cputime << 12; S390_lowcore.steal_clock -= cputime << 12; - account_system_time(tsk, 0, cputime, cputime); + if (in_irq() || idle_task(smp_processor_id()) != current) + account_system_time(tsk, 0, cputime, cputime); + else + account_idle_time(cputime); } EXPORT_SYMBOL_GPL(account_system_vtime); diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index c9f7cda48ed7..732e52dc991a 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -132,8 +132,7 @@ static void do_stolen_accounting(void) *snap = state; /* Add the appropriate number of ticks of stolen time, - including any left-overs from last time. Passing NULL to - account_steal_time accounts the time as stolen. */ + including any left-overs from last time. */ stolen = runnable + offline + __get_cpu_var(residual_stolen); if (stolen < 0) @@ -141,11 +140,10 @@ static void do_stolen_accounting(void) ticks = iter_div_u64_rem(stolen, NS_PER_TICK, &stolen); __get_cpu_var(residual_stolen) = stolen; - account_steal_time(NULL, ticks); + account_steal_ticks(ticks); /* Add the appropriate number of ticks of blocked time, - including any left-overs from last time. Passing idle to - account_steal_time accounts the time as idle/wait. */ + including any left-overs from last time. */ blocked += __get_cpu_var(residual_blocked); if (blocked < 0) @@ -153,7 +151,7 @@ static void do_stolen_accounting(void) ticks = iter_div_u64_rem(blocked, NS_PER_TICK, &blocked); __get_cpu_var(residual_blocked) = blocked; - account_steal_time(idle_task(smp_processor_id()), ticks); + account_idle_ticks(ticks); } /* diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index c78a459662a6..570d20413119 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h @@ -81,6 +81,11 @@ static inline unsigned int kstat_irqs(unsigned int irq) extern unsigned long long task_delta_exec(struct task_struct *); extern void account_user_time(struct task_struct *, cputime_t, cputime_t); extern void account_system_time(struct task_struct *, int, cputime_t, cputime_t); -extern void account_steal_time(struct task_struct *, cputime_t); +extern void account_steal_time(cputime_t); +extern void account_idle_time(cputime_t); + +extern void account_process_tick(struct task_struct *, int user); +extern void account_steal_ticks(unsigned long ticks); +extern void account_idle_ticks(unsigned long ticks); #endif /* _LINUX_KERNEL_STAT_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 8395e715809d..b475d4db8053 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -284,7 +284,6 @@ long io_schedule_timeout(long timeout); extern void cpu_init (void); extern void trap_init(void); -extern void account_process_tick(struct task_struct *task, int user); extern void update_process_times(int user); extern void scheduler_tick(void); diff --git a/kernel/sched.c b/kernel/sched.c index 5b03679ff712..635eaffe1e4c 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4139,7 +4139,6 @@ void account_system_time(struct task_struct *p, int hardirq_offset, cputime_t cputime, cputime_t cputime_scaled) { struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; - struct rq *rq = this_rq(); cputime64_t tmp; if ((p->flags & PF_VCPU) && (irq_count() - hardirq_offset == 0)) { @@ -4158,37 +4157,84 @@ void account_system_time(struct task_struct *p, int hardirq_offset, cpustat->irq = cputime64_add(cpustat->irq, tmp); else if (softirq_count()) cpustat->softirq = cputime64_add(cpustat->softirq, tmp); - else if (p != rq->idle) - cpustat->system = cputime64_add(cpustat->system, tmp); - else if (atomic_read(&rq->nr_iowait) > 0) - cpustat->iowait = cputime64_add(cpustat->iowait, tmp); else - cpustat->idle = cputime64_add(cpustat->idle, tmp); + cpustat->system = cputime64_add(cpustat->system, tmp); + /* Account for system time used */ acct_update_integrals(p); } /* * Account for involuntary wait time. - * @p: the process from which the cpu time has been stolen * @steal: the cpu time spent in involuntary wait */ -void account_steal_time(struct task_struct *p, cputime_t steal) +void account_steal_time(cputime_t cputime) +{ + struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; + cputime64_t cputime64 = cputime_to_cputime64(cputime); + + cpustat->steal = cputime64_add(cpustat->steal, cputime64); +} + +/* + * Account for idle time. + * @cputime: the cpu time spent in idle wait + */ +void account_idle_time(cputime_t cputime) { struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; - cputime64_t tmp = cputime_to_cputime64(steal); + cputime64_t cputime64 = cputime_to_cputime64(cputime); struct rq *rq = this_rq(); - if (p == rq->idle) { - p->stime = cputime_add(p->stime, steal); - if (atomic_read(&rq->nr_iowait) > 0) - cpustat->iowait = cputime64_add(cpustat->iowait, tmp); - else - cpustat->idle = cputime64_add(cpustat->idle, tmp); - } else - cpustat->steal = cputime64_add(cpustat->steal, tmp); + if (atomic_read(&rq->nr_iowait) > 0) + cpustat->iowait = cputime64_add(cpustat->iowait, cputime64); + else + cpustat->idle = cputime64_add(cpustat->idle, cputime64); +} + +#ifndef CONFIG_VIRT_CPU_ACCOUNTING + +/* + * Account a single tick of cpu time. + * @p: the process that the cpu time gets accounted to + * @user_tick: indicates if the tick is a user or a system tick + */ +void account_process_tick(struct task_struct *p, int user_tick) +{ + cputime_t one_jiffy = jiffies_to_cputime(1); + cputime_t one_jiffy_scaled = cputime_to_scaled(one_jiffy); + struct rq *rq = this_rq(); + + if (user_tick) + account_user_time(p, one_jiffy, one_jiffy_scaled); + else if (p != rq->idle) + account_system_time(p, HARDIRQ_OFFSET, one_jiffy, + one_jiffy_scaled); + else + account_idle_time(one_jiffy); +} + +/* + * Account multiple ticks of steal time. + * @p: the process from which the cpu time has been stolen + * @ticks: number of stolen ticks + */ +void account_steal_ticks(unsigned long ticks) +{ + account_steal_time(jiffies_to_cputime(ticks)); +} + +/* + * Account multiple ticks of idle time. + * @ticks: number of stolen ticks + */ +void account_idle_ticks(unsigned long ticks) +{ + account_idle_time(jiffies_to_cputime(ticks)); } +#endif + /* * Use precise platform statistics if available: */ diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 1f2fce2479fe..611fa4c0baab 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -419,8 +419,9 @@ void tick_nohz_restart_sched_tick(void) { int cpu = smp_processor_id(); struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); +#ifndef CONFIG_VIRT_CPU_ACCOUNTING unsigned long ticks; - cputime_t cputime; +#endif ktime_t now; local_irq_disable(); @@ -442,6 +443,7 @@ void tick_nohz_restart_sched_tick(void) tick_do_update_jiffies64(now); cpu_clear(cpu, nohz_cpu_mask); +#ifndef CONFIG_VIRT_CPU_ACCOUNTING /* * We stopped the tick in idle. Update process times would miss the * time we slept as update_process_times does only a 1 tick @@ -451,12 +453,9 @@ void tick_nohz_restart_sched_tick(void) /* * We might be one off. Do not randomly account a huge number of ticks! */ - if (ticks && ticks < LONG_MAX) { - add_preempt_count(HARDIRQ_OFFSET); - cputime = jiffies_to_cputime(ticks); - account_system_time(current, HARDIRQ_OFFSET, cputime, cputime); - sub_preempt_count(HARDIRQ_OFFSET); - } + if (ticks && ticks < LONG_MAX) + account_idle_ticks(ticks); +#endif touch_softlockup_watchdog(); /* diff --git a/kernel/timer.c b/kernel/timer.c index b5efb528aa1d..dee3f641a7a7 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1018,19 +1018,6 @@ unsigned long get_next_timer_interrupt(unsigned long now) } #endif -#ifndef CONFIG_VIRT_CPU_ACCOUNTING -void account_process_tick(struct task_struct *p, int user_tick) -{ - cputime_t one_jiffy = jiffies_to_cputime(1); - - if (user_tick) - account_user_time(p, one_jiffy, cputime_to_scaled(one_jiffy)); - else - account_system_time(p, HARDIRQ_OFFSET, one_jiffy, - cputime_to_scaled(one_jiffy)); -} -#endif - /* * Called from the timer interrupt handler to charge one tick to the current * process. user_tick is 1 if the tick is user time, 0 for system. -- cgit v1.2.3 From c4abb7c9cde24b7351a47328ef866e6a2bbb1ad0 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 26 Sep 2008 09:30:55 +0200 Subject: KVM: x86: Support for user space injected NMIs Introduces the KVM_NMI IOCTL to the generic x86 part of KVM for injecting NMIs from user space and also extends the statistic report accordingly. Based on the original patch by Sheng Yang. Signed-off-by: Jan Kiszka Signed-off-by: Sheng Yang Signed-off-by: Avi Kivity --- arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/x86.c | 46 +++++++++++++++++++++++++++++++++++++++-- include/linux/kvm.h | 11 ++++++++-- 3 files changed, 55 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index bfbbdea869bf..a40fa8478920 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -398,6 +398,7 @@ struct kvm_vcpu_stat { u32 halt_exits; u32 halt_wakeup; u32 request_irq_exits; + u32 request_nmi_exits; u32 irq_exits; u32 host_state_reload; u32 efer_reload; @@ -406,6 +407,7 @@ struct kvm_vcpu_stat { u32 insn_emulation_fail; u32 hypercalls; u32 irq_injections; + u32 nmi_injections; }; struct descriptor_table { diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 1fa9a6db633d..07971451b947 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -86,6 +86,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { { "halt_wakeup", VCPU_STAT(halt_wakeup) }, { "hypercalls", VCPU_STAT(hypercalls) }, { "request_irq", VCPU_STAT(request_irq_exits) }, + { "request_nmi", VCPU_STAT(request_nmi_exits) }, { "irq_exits", VCPU_STAT(irq_exits) }, { "host_state_reload", VCPU_STAT(host_state_reload) }, { "efer_reload", VCPU_STAT(efer_reload) }, @@ -93,6 +94,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { { "insn_emulation", VCPU_STAT(insn_emulation) }, { "insn_emulation_fail", VCPU_STAT(insn_emulation_fail) }, { "irq_injections", VCPU_STAT(irq_injections) }, + { "nmi_injections", VCPU_STAT(nmi_injections) }, { "mmu_shadow_zapped", VM_STAT(mmu_shadow_zapped) }, { "mmu_pte_write", VM_STAT(mmu_pte_write) }, { "mmu_pte_updated", VM_STAT(mmu_pte_updated) }, @@ -1318,6 +1320,15 @@ static int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, return 0; } +static int kvm_vcpu_ioctl_nmi(struct kvm_vcpu *vcpu) +{ + vcpu_load(vcpu); + kvm_inject_nmi(vcpu); + vcpu_put(vcpu); + + return 0; +} + static int vcpu_ioctl_tpr_access_reporting(struct kvm_vcpu *vcpu, struct kvm_tpr_access_ctl *tac) { @@ -1377,6 +1388,13 @@ long kvm_arch_vcpu_ioctl(struct file *filp, r = 0; break; } + case KVM_NMI: { + r = kvm_vcpu_ioctl_nmi(vcpu); + if (r) + goto out; + r = 0; + break; + } case KVM_SET_CPUID: { struct kvm_cpuid __user *cpuid_arg = argp; struct kvm_cpuid cpuid; @@ -2812,18 +2830,37 @@ static int dm_request_for_irq_injection(struct kvm_vcpu *vcpu, (kvm_x86_ops->get_rflags(vcpu) & X86_EFLAGS_IF)); } +/* + * Check if userspace requested a NMI window, and that the NMI window + * is open. + * + * No need to exit to userspace if we already have a NMI queued. + */ +static int dm_request_for_nmi_injection(struct kvm_vcpu *vcpu, + struct kvm_run *kvm_run) +{ + return (!vcpu->arch.nmi_pending && + kvm_run->request_nmi_window && + vcpu->arch.nmi_window_open); +} + static void post_kvm_run_save(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) { kvm_run->if_flag = (kvm_x86_ops->get_rflags(vcpu) & X86_EFLAGS_IF) != 0; kvm_run->cr8 = kvm_get_cr8(vcpu); kvm_run->apic_base = kvm_get_apic_base(vcpu); - if (irqchip_in_kernel(vcpu->kvm)) + if (irqchip_in_kernel(vcpu->kvm)) { kvm_run->ready_for_interrupt_injection = 1; - else + kvm_run->ready_for_nmi_injection = 1; + } else { kvm_run->ready_for_interrupt_injection = (vcpu->arch.interrupt_window_open && vcpu->arch.irq_summary == 0); + kvm_run->ready_for_nmi_injection = + (vcpu->arch.nmi_window_open && + vcpu->arch.nmi_pending == 0); + } } static void vapic_enter(struct kvm_vcpu *vcpu) @@ -2999,6 +3036,11 @@ static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) } if (r > 0) { + if (dm_request_for_nmi_injection(vcpu, kvm_run)) { + r = -EINTR; + kvm_run->exit_reason = KVM_EXIT_NMI; + ++vcpu->stat.request_nmi_exits; + } if (dm_request_for_irq_injection(vcpu, kvm_run)) { r = -EINTR; kvm_run->exit_reason = KVM_EXIT_INTR; diff --git a/include/linux/kvm.h b/include/linux/kvm.h index f18b86fa8655..44fd7fa0af2b 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -83,18 +83,22 @@ struct kvm_irqchip { #define KVM_EXIT_S390_SIEIC 13 #define KVM_EXIT_S390_RESET 14 #define KVM_EXIT_DCR 15 +#define KVM_EXIT_NMI 16 +#define KVM_EXIT_NMI_WINDOW_OPEN 17 /* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ struct kvm_run { /* in */ __u8 request_interrupt_window; - __u8 padding1[7]; + __u8 request_nmi_window; + __u8 padding1[6]; /* out */ __u32 exit_reason; __u8 ready_for_interrupt_injection; __u8 if_flag; - __u8 padding2[2]; + __u8 ready_for_nmi_injection; + __u8 padding2; /* in (pre_kvm_run), out (post_kvm_run) */ __u64 cr8; @@ -387,6 +391,7 @@ struct kvm_trace_rec { #define KVM_CAP_DEVICE_ASSIGNMENT 17 #endif #define KVM_CAP_IOMMU 18 +#define KVM_CAP_NMI 19 /* * ioctls for VM fds @@ -458,6 +463,8 @@ struct kvm_trace_rec { #define KVM_S390_INITIAL_RESET _IO(KVMIO, 0x97) #define KVM_GET_MP_STATE _IOR(KVMIO, 0x98, struct kvm_mp_state) #define KVM_SET_MP_STATE _IOW(KVMIO, 0x99, struct kvm_mp_state) +/* Available with KVM_CAP_NMI */ +#define KVM_NMI _IO(KVMIO, 0x9a) #define KVM_TRC_INJ_VIRQ (KVM_TRC_HANDLER + 0x02) #define KVM_TRC_REDELIVER_EVT (KVM_TRC_HANDLER + 0x03) -- cgit v1.2.3 From e19e30effac03f5a005a8e42ed941a2a5dc62654 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Mon, 20 Oct 2008 16:07:10 +0800 Subject: KVM: IRQ ACK notifier should be used with in-kernel irqchip Also remove unnecessary parameter of unregister irq ack notifier. Signed-off-by: Sheng Yang Signed-off-by: Avi Kivity --- include/linux/kvm_host.h | 3 +-- virt/kvm/irq_comm.c | 8 ++++++-- virt/kvm/kvm_main.c | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index bb92be2153bc..3a0fb77d1f6a 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -316,8 +316,7 @@ void kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level); void kvm_notify_acked_irq(struct kvm *kvm, unsigned gsi); void kvm_register_irq_ack_notifier(struct kvm *kvm, struct kvm_irq_ack_notifier *kian); -void kvm_unregister_irq_ack_notifier(struct kvm *kvm, - struct kvm_irq_ack_notifier *kian); +void kvm_unregister_irq_ack_notifier(struct kvm_irq_ack_notifier *kian); int kvm_request_irq_source_id(struct kvm *kvm); void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); diff --git a/virt/kvm/irq_comm.c b/virt/kvm/irq_comm.c index 55ad76ee2d09..9fbbdea3d1d5 100644 --- a/virt/kvm/irq_comm.c +++ b/virt/kvm/irq_comm.c @@ -58,12 +58,16 @@ void kvm_notify_acked_irq(struct kvm *kvm, unsigned gsi) void kvm_register_irq_ack_notifier(struct kvm *kvm, struct kvm_irq_ack_notifier *kian) { + /* Must be called with in-kernel IRQ chip, otherwise it's nonsense */ + ASSERT(irqchip_in_kernel(kvm)); + ASSERT(kian); hlist_add_head(&kian->link, &kvm->arch.irq_ack_notifier_list); } -void kvm_unregister_irq_ack_notifier(struct kvm *kvm, - struct kvm_irq_ack_notifier *kian) +void kvm_unregister_irq_ack_notifier(struct kvm_irq_ack_notifier *kian) { + if (!kian) + return; hlist_del(&kian->link); } diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index a87f45edfae8..4f43abe198e4 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -143,7 +143,7 @@ static void kvm_free_assigned_device(struct kvm *kvm, if (irqchip_in_kernel(kvm) && assigned_dev->irq_requested) free_irq(assigned_dev->host_irq, (void *)assigned_dev); - kvm_unregister_irq_ack_notifier(kvm, &assigned_dev->ack_notifier); + kvm_unregister_irq_ack_notifier(&assigned_dev->ack_notifier); kvm_free_irq_source_id(kvm, assigned_dev->irq_source_id); if (cancel_work_sync(&assigned_dev->interrupt_work)) -- cgit v1.2.3 From 4f906c19ae29397409bedabf7bbe5cb42ad90332 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Mon, 24 Nov 2008 14:32:51 +0800 Subject: KVM: Replace irq_requested with more generic irq_requested_type Separate guest irq type and host irq type, for we can support guest using INTx with host using MSI (but not opposite combination). Signed-off-by: Sheng Yang Signed-off-by: Avi Kivity --- include/linux/kvm_host.h | 4 +++- virt/kvm/kvm_main.c | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 3a0fb77d1f6a..c3d4b96a08fa 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -307,7 +307,9 @@ struct kvm_assigned_dev_kernel { int host_devfn; int host_irq; int guest_irq; - int irq_requested; +#define KVM_ASSIGNED_DEV_GUEST_INTX (1 << 0) +#define KVM_ASSIGNED_DEV_HOST_INTX (1 << 8) + unsigned long irq_requested_type; int irq_source_id; struct pci_dev *dev; struct kvm *kvm; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index ef2f03cf42c0..638de47e167d 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -140,7 +140,7 @@ static void kvm_free_assigned_device(struct kvm *kvm, struct kvm_assigned_dev_kernel *assigned_dev) { - if (irqchip_in_kernel(kvm) && assigned_dev->irq_requested) + if (irqchip_in_kernel(kvm) && assigned_dev->irq_requested_type) free_irq(assigned_dev->host_irq, (void *)assigned_dev); kvm_unregister_irq_ack_notifier(&assigned_dev->ack_notifier); @@ -180,7 +180,7 @@ static int assigned_device_update_intx(struct kvm *kvm, struct kvm_assigned_dev_kernel *adev, struct kvm_assigned_irq *airq) { - if (adev->irq_requested) { + if (adev->irq_requested_type & KVM_ASSIGNED_DEV_GUEST_INTX) { adev->guest_irq = airq->guest_irq; adev->ack_notifier.gsi = airq->guest_irq; return 0; @@ -207,7 +207,8 @@ static int assigned_device_update_intx(struct kvm *kvm, return -EIO; } - adev->irq_requested = true; + adev->irq_requested_type = KVM_ASSIGNED_DEV_GUEST_INTX | + KVM_ASSIGNED_DEV_HOST_INTX; return 0; } @@ -227,7 +228,7 @@ static int kvm_vm_ioctl_assign_irq(struct kvm *kvm, return -EINVAL; } - if (!match->irq_requested) { + if (!match->irq_requested_type) { INIT_WORK(&match->interrupt_work, kvm_assigned_dev_interrupt_work_handler); if (irqchip_in_kernel(kvm)) { -- cgit v1.2.3 From 0937c48d075ddd59ae2c12a6fa8308b9c7a63753 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Mon, 24 Nov 2008 14:32:53 +0800 Subject: KVM: Add fields for MSI device assignment Prepared for kvm_arch_assigned_device_msi_dispatch(). Signed-off-by: Sheng Yang Signed-off-by: Avi Kivity --- include/linux/kvm.h | 7 +++++++ include/linux/kvm_host.h | 4 ++++ 2 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 44fd7fa0af2b..bb283c388a24 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -507,10 +507,17 @@ struct kvm_assigned_irq { __u32 guest_irq; __u32 flags; union { + struct { + __u32 addr_lo; + __u32 addr_hi; + __u32 data; + } guest_msi; __u32 reserved[12]; }; }; #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) +#define KVM_DEV_IRQ_ASSIGN_ENABLE_MSI (1 << 0) + #endif diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c3d4b96a08fa..8091a4d90ddf 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -307,8 +308,11 @@ struct kvm_assigned_dev_kernel { int host_devfn; int host_irq; int guest_irq; + struct msi_msg guest_msi; #define KVM_ASSIGNED_DEV_GUEST_INTX (1 << 0) +#define KVM_ASSIGNED_DEV_GUEST_MSI (1 << 1) #define KVM_ASSIGNED_DEV_HOST_INTX (1 << 8) +#define KVM_ASSIGNED_DEV_HOST_MSI (1 << 9) unsigned long irq_requested_type; int irq_source_id; struct pci_dev *dev; -- cgit v1.2.3 From 6b9cc7fd469869bed38831c5adac3f59dc25eaf5 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Mon, 24 Nov 2008 14:32:56 +0800 Subject: KVM: Enable MSI for device assignment We enable guest MSI and host MSI support in this patch. The userspace want to enable MSI should set KVM_DEV_IRQ_ASSIGN_ENABLE_MSI in the assigned_irq's flag. Function would return -ENOTTY if can't enable MSI, userspace shouldn't set MSI Enable bit when KVM_ASSIGN_IRQ return -ENOTTY with KVM_DEV_IRQ_ASSIGN_ENABLE_MSI. Userspace can tell the support of MSI device from #ifdef KVM_CAP_DEVICE_MSI. Signed-off-by: Sheng Yang Signed-off-by: Avi Kivity --- include/linux/kvm.h | 3 ++ virt/kvm/kvm_main.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 78 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm.h b/include/linux/kvm.h index bb283c388a24..0997e6f5490c 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -392,6 +392,9 @@ struct kvm_trace_rec { #endif #define KVM_CAP_IOMMU 18 #define KVM_CAP_NMI 19 +#if defined(CONFIG_X86) +#define KVM_CAP_DEVICE_MSI 20 +#endif /* * ioctls for VM fds diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 228c1d18a614..bf36ae9ae7df 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -159,9 +159,15 @@ static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work) * finer-grained lock, update this */ mutex_lock(&assigned_dev->kvm->lock); - kvm_set_irq(assigned_dev->kvm, - assigned_dev->irq_source_id, - assigned_dev->guest_irq, 1); + if (assigned_dev->irq_requested_type & KVM_ASSIGNED_DEV_GUEST_INTX) + kvm_set_irq(assigned_dev->kvm, + assigned_dev->irq_source_id, + assigned_dev->guest_irq, 1); + else if (assigned_dev->irq_requested_type & + KVM_ASSIGNED_DEV_GUEST_MSI) { + assigned_device_msi_dispatch(assigned_dev); + enable_irq(assigned_dev->host_irq); + } mutex_unlock(&assigned_dev->kvm->lock); kvm_put_kvm(assigned_dev->kvm); } @@ -197,6 +203,8 @@ static void kvm_free_assigned_device(struct kvm *kvm, { if (irqchip_in_kernel(kvm) && assigned_dev->irq_requested_type) free_irq(assigned_dev->host_irq, (void *)assigned_dev); + if (assigned_dev->irq_requested_type & KVM_ASSIGNED_DEV_HOST_MSI) + pci_disable_msi(assigned_dev->dev); kvm_unregister_irq_ack_notifier(&assigned_dev->ack_notifier); kvm_free_irq_source_id(kvm, assigned_dev->irq_source_id); @@ -242,6 +250,11 @@ static int assigned_device_update_intx(struct kvm *kvm, return 0; if (irqchip_in_kernel(kvm)) { + if (adev->irq_requested_type & KVM_ASSIGNED_DEV_HOST_MSI) { + free_irq(adev->host_irq, (void *)kvm); + pci_disable_msi(adev->dev); + } + if (!capable(CAP_SYS_RAWIO)) return -EPERM; @@ -265,6 +278,41 @@ static int assigned_device_update_intx(struct kvm *kvm, return 0; } +#ifdef CONFIG_X86 +static int assigned_device_update_msi(struct kvm *kvm, + struct kvm_assigned_dev_kernel *adev, + struct kvm_assigned_irq *airq) +{ + int r; + + /* x86 don't care upper address of guest msi message addr */ + adev->guest_msi.address_lo = airq->guest_msi.addr_lo; + adev->guest_msi.data = airq->guest_msi.data; + adev->ack_notifier.gsi = -1; + + if (adev->irq_requested_type & KVM_ASSIGNED_DEV_HOST_MSI) + return 0; + + if (irqchip_in_kernel(kvm)) { + if (adev->irq_requested_type & KVM_ASSIGNED_DEV_HOST_INTX) + free_irq(adev->host_irq, (void *)adev); + + r = pci_enable_msi(adev->dev); + if (r) + return r; + + adev->host_irq = adev->dev->irq; + if (request_irq(adev->host_irq, kvm_assigned_dev_intr, 0, + "kvm_assigned_msi_device", (void *)adev)) + return -EIO; + } + + adev->irq_requested_type = KVM_ASSIGNED_DEV_GUEST_MSI | + KVM_ASSIGNED_DEV_HOST_MSI; + return 0; +} +#endif + static int kvm_vm_ioctl_assign_irq(struct kvm *kvm, struct kvm_assigned_irq *assigned_irq) @@ -301,9 +349,30 @@ static int kvm_vm_ioctl_assign_irq(struct kvm *kvm, } } - r = assigned_device_update_intx(kvm, match, assigned_irq); - if (r) - goto out_release; + if (assigned_irq->flags & KVM_DEV_IRQ_ASSIGN_ENABLE_MSI) { +#ifdef CONFIG_X86 + r = assigned_device_update_msi(kvm, match, assigned_irq); + if (r) { + printk(KERN_WARNING "kvm: failed to enable " + "MSI device!\n"); + goto out_release; + } +#else + r = -ENOTTY; +#endif + } else if (assigned_irq->host_irq == 0 && match->dev->irq == 0) { + /* Host device IRQ 0 means don't support INTx */ + printk(KERN_WARNING "kvm: wait device to enable MSI!\n"); + r = 0; + } else { + /* Non-sharing INTx mode */ + r = assigned_device_update_intx(kvm, match, assigned_irq); + if (r) { + printk(KERN_WARNING "kvm: failed to enable " + "INTx device!\n"); + goto out_release; + } + } mutex_unlock(&kvm->lock); return r; -- cgit v1.2.3 From 1a811b6167089bcdb84284f2dc9fd0b4d0f1899d Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Mon, 8 Dec 2008 18:25:27 +0200 Subject: KVM: Advertise the bug in memory region destruction as fixed Userspace might need to act differently. Signed-off-by: Avi Kivity --- include/linux/kvm.h | 2 ++ virt/kvm/kvm_main.c | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 0997e6f5490c..48807767e726 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -395,6 +395,8 @@ struct kvm_trace_rec { #if defined(CONFIG_X86) #define KVM_CAP_DEVICE_MSI 20 #endif +/* Bug in KVM_SET_USER_MEMORY_REGION fixed: */ +#define KVM_CAP_DESTROY_MEMORY_REGION_WORKS 21 /* * ioctls for VM fds diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index e7644b90667e..e066eb125e55 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1905,6 +1905,17 @@ static int kvm_dev_ioctl_create_vm(void) return fd; } +static long kvm_dev_ioctl_check_extension_generic(long arg) +{ + switch (arg) { + case KVM_CAP_DESTROY_MEMORY_REGION_WORKS: + return 1; + default: + break; + } + return kvm_dev_ioctl_check_extension(arg); +} + static long kvm_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -1924,7 +1935,7 @@ static long kvm_dev_ioctl(struct file *filp, r = kvm_dev_ioctl_create_vm(); break; case KVM_CHECK_EXTENSION: - r = kvm_dev_ioctl_check_extension(arg); + r = kvm_dev_ioctl_check_extension_generic(arg); break; case KVM_GET_VCPU_MMAP_SIZE: r = -EINVAL; -- cgit v1.2.3 From defaf1587c5d7dff828f6f11c8941e5bcef00f50 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Tue, 2 Dec 2008 12:16:33 +0000 Subject: KVM: fix handling of ACK from shared guest IRQ If an assigned device shares a guest irq with an emulated device then we currently interpret an ack generated by the emulated device as originating from the assigned device leading to e.g. "Unbalanced enable for IRQ 4347" from the enable_irq() in kvm_assigned_dev_ack_irq(). The fix is fairly simple - don't enable the physical device irq unless it was previously disabled. Of course, this can still lead to a situation where a non-assigned device ACK can cause the physical device irq to be reenabled before the device was serviced. However, being level sensitive, the interrupt will merely be regenerated. Signed-off-by: Mark McLoughlin Signed-off-by: Avi Kivity --- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 8091a4d90ddf..eafabd5c66b2 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -307,6 +307,7 @@ struct kvm_assigned_dev_kernel { int host_busnr; int host_devfn; int host_irq; + bool host_irq_disabled; int guest_irq; struct msi_msg guest_msi; #define KVM_ASSIGNED_DEV_GUEST_INTX (1 << 0) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index eb70ca6c7145..fc6127cbea1f 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -170,6 +170,7 @@ static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work) KVM_ASSIGNED_DEV_GUEST_MSI) { assigned_device_msi_dispatch(assigned_dev); enable_irq(assigned_dev->host_irq); + assigned_dev->host_irq_disabled = false; } mutex_unlock(&assigned_dev->kvm->lock); kvm_put_kvm(assigned_dev->kvm); @@ -181,8 +182,12 @@ static irqreturn_t kvm_assigned_dev_intr(int irq, void *dev_id) (struct kvm_assigned_dev_kernel *) dev_id; kvm_get_kvm(assigned_dev->kvm); + schedule_work(&assigned_dev->interrupt_work); + disable_irq_nosync(irq); + assigned_dev->host_irq_disabled = true; + return IRQ_HANDLED; } @@ -196,8 +201,16 @@ static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian) dev = container_of(kian, struct kvm_assigned_dev_kernel, ack_notifier); + kvm_set_irq(dev->kvm, dev->irq_source_id, dev->guest_irq, 0); - enable_irq(dev->host_irq); + + /* The guest irq may be shared so this ack may be + * from another device. + */ + if (dev->host_irq_disabled) { + enable_irq(dev->host_irq); + dev->host_irq_disabled = false; + } } static void kvm_free_assigned_irq(struct kvm *kvm, -- cgit v1.2.3 From 4531220b71f0399e71cda0c4cf749e7281a7416a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Thu, 11 Dec 2008 16:54:54 +0100 Subject: KVM: x86: Rework user space NMI injection as KVM_CAP_USER_NMI There is no point in doing the ready_for_nmi_injection/ request_nmi_window dance with user space. First, we don't do this for in-kernel irqchip anyway, while the code path is the same as for user space irqchip mode. And second, there is nothing to loose if a pending NMI is overwritten by another one (in contrast to IRQs where we have to save the number). Actually, there is even the risk of raising spurious NMIs this way because the reason for the held-back NMI might already be handled while processing the first one. Therefore this patch creates a simplified user space NMI injection interface, exporting it under KVM_CAP_USER_NMI and dropping the old KVM_CAP_NMI capability. And this time we also take care to provide the interface only on archs supporting NMIs via KVM (right now only x86). Signed-off-by: Jan Kiszka Signed-off-by: Avi Kivity --- arch/x86/kvm/vmx.c | 24 ++---------------------- arch/x86/kvm/x86.c | 28 ++-------------------------- include/linux/kvm.h | 11 +++++------ 3 files changed, 9 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 487e1dcdce33..6259d7467648 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2498,15 +2498,13 @@ static void do_interrupt_requests(struct kvm_vcpu *vcpu, } if (vcpu->arch.nmi_injected) { vmx_inject_nmi(vcpu); - if (vcpu->arch.nmi_pending || kvm_run->request_nmi_window) + if (vcpu->arch.nmi_pending) enable_nmi_window(vcpu); else if (vcpu->arch.irq_summary || kvm_run->request_interrupt_window) enable_irq_window(vcpu); return; } - if (!vcpu->arch.nmi_window_open || kvm_run->request_nmi_window) - enable_nmi_window(vcpu); if (vcpu->arch.interrupt_window_open) { if (vcpu->arch.irq_summary && !vcpu->arch.interrupt.pending) @@ -3040,14 +3038,6 @@ static int handle_nmi_window(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control); ++vcpu->stat.nmi_window_exits; - /* - * If the user space waits to inject a NMI, exit as soon as possible - */ - if (kvm_run->request_nmi_window && !vcpu->arch.nmi_pending) { - kvm_run->exit_reason = KVM_EXIT_NMI_WINDOW_OPEN; - return 0; - } - return 1; } @@ -3162,7 +3152,7 @@ static int kvm_handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) vmx->soft_vnmi_blocked = 0; vcpu->arch.nmi_window_open = 1; } else if (vmx->vnmi_blocked_time > 1000000000LL && - (kvm_run->request_nmi_window || vcpu->arch.nmi_pending)) { + vcpu->arch.nmi_pending) { /* * This CPU don't support us in finding the end of an * NMI-blocked window if the guest runs with IRQs @@ -3175,16 +3165,6 @@ static int kvm_handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) vmx->soft_vnmi_blocked = 0; vmx->vcpu.arch.nmi_window_open = 1; } - - /* - * If the user space waits to inject an NNI, exit ASAP - */ - if (vcpu->arch.nmi_window_open && kvm_run->request_nmi_window - && !vcpu->arch.nmi_pending) { - kvm_run->exit_reason = KVM_EXIT_NMI_WINDOW_OPEN; - ++vcpu->stat.nmi_window_exits; - return 0; - } } if (exit_reason < kvm_vmx_max_exit_handlers diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 10302d3bd415..0e6aa8141dcd 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2887,37 +2887,18 @@ static int dm_request_for_irq_injection(struct kvm_vcpu *vcpu, (kvm_x86_ops->get_rflags(vcpu) & X86_EFLAGS_IF)); } -/* - * Check if userspace requested a NMI window, and that the NMI window - * is open. - * - * No need to exit to userspace if we already have a NMI queued. - */ -static int dm_request_for_nmi_injection(struct kvm_vcpu *vcpu, - struct kvm_run *kvm_run) -{ - return (!vcpu->arch.nmi_pending && - kvm_run->request_nmi_window && - vcpu->arch.nmi_window_open); -} - static void post_kvm_run_save(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) { kvm_run->if_flag = (kvm_x86_ops->get_rflags(vcpu) & X86_EFLAGS_IF) != 0; kvm_run->cr8 = kvm_get_cr8(vcpu); kvm_run->apic_base = kvm_get_apic_base(vcpu); - if (irqchip_in_kernel(vcpu->kvm)) { + if (irqchip_in_kernel(vcpu->kvm)) kvm_run->ready_for_interrupt_injection = 1; - kvm_run->ready_for_nmi_injection = 1; - } else { + else kvm_run->ready_for_interrupt_injection = (vcpu->arch.interrupt_window_open && vcpu->arch.irq_summary == 0); - kvm_run->ready_for_nmi_injection = - (vcpu->arch.nmi_window_open && - vcpu->arch.nmi_pending == 0); - } } static void vapic_enter(struct kvm_vcpu *vcpu) @@ -3093,11 +3074,6 @@ static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) } if (r > 0) { - if (dm_request_for_nmi_injection(vcpu, kvm_run)) { - r = -EINTR; - kvm_run->exit_reason = KVM_EXIT_NMI; - ++vcpu->stat.request_nmi_exits; - } if (dm_request_for_irq_injection(vcpu, kvm_run)) { r = -EINTR; kvm_run->exit_reason = KVM_EXIT_INTR; diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 48807767e726..35525ac63337 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -84,21 +84,18 @@ struct kvm_irqchip { #define KVM_EXIT_S390_RESET 14 #define KVM_EXIT_DCR 15 #define KVM_EXIT_NMI 16 -#define KVM_EXIT_NMI_WINDOW_OPEN 17 /* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ struct kvm_run { /* in */ __u8 request_interrupt_window; - __u8 request_nmi_window; - __u8 padding1[6]; + __u8 padding1[7]; /* out */ __u32 exit_reason; __u8 ready_for_interrupt_injection; __u8 if_flag; - __u8 ready_for_nmi_injection; - __u8 padding2; + __u8 padding2[2]; /* in (pre_kvm_run), out (post_kvm_run) */ __u64 cr8; @@ -391,12 +388,14 @@ struct kvm_trace_rec { #define KVM_CAP_DEVICE_ASSIGNMENT 17 #endif #define KVM_CAP_IOMMU 18 -#define KVM_CAP_NMI 19 #if defined(CONFIG_X86) #define KVM_CAP_DEVICE_MSI 20 #endif /* Bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_DESTROY_MEMORY_REGION_WORKS 21 +#if defined(CONFIG_X86) +#define KVM_CAP_USER_NMI 22 +#endif /* * ioctls for VM fds -- cgit v1.2.3 From b30f8af3358b5c66be223e3a9f3d11b3d02b4a8f Mon Sep 17 00:00:00 2001 From: Jarkko Lavinen Date: Mon, 17 Nov 2008 14:35:21 +0200 Subject: mmc: Add 8-bit bus width support Signed-off-by: Jarkko Lavinen Signed-off-by: Pierre Ossman --- drivers/mmc/core/mmc.c | 18 ++++++++++++++---- include/linux/mmc/host.h | 2 ++ 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index fdd7c760be8c..c232d11a7ed4 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -434,13 +434,24 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * Activate wide bus (if supported). */ if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && - (host->caps & MMC_CAP_4_BIT_DATA)) { + (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { + unsigned ext_csd_bit, bus_width; + + if (host->caps & MMC_CAP_8_BIT_DATA) { + ext_csd_bit = EXT_CSD_BUS_WIDTH_8; + bus_width = MMC_BUS_WIDTH_8; + } else { + ext_csd_bit = EXT_CSD_BUS_WIDTH_4; + bus_width = MMC_BUS_WIDTH_4; + } + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); + EXT_CSD_BUS_WIDTH, ext_csd_bit); + if (err) goto free_card; - mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); + mmc_set_bus_width(card->host, bus_width); } if (!oldcard) @@ -624,4 +635,3 @@ err: return err; } - diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index f842f234e44f..4e457256bd33 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -41,6 +41,7 @@ struct mmc_ios { #define MMC_BUS_WIDTH_1 0 #define MMC_BUS_WIDTH_4 2 +#define MMC_BUS_WIDTH_8 3 unsigned char timing; /* timing specification used */ @@ -116,6 +117,7 @@ struct mmc_host { #define MMC_CAP_SDIO_IRQ (1 << 3) /* Can signal pending SDIO IRQs */ #define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */ #define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */ +#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */ /* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ -- cgit v1.2.3 From 86e8286a0e48663e1e86a5884b30a6d05de2993a Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Wed, 26 Nov 2008 22:54:17 +0300 Subject: mmc: Add mmc_vddrange_to_ocrmask() helper function This function sets the OCR mask bits according to provided voltage ranges. Will be used by the mmc_spi OpenFirmware bindings. Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/core/core.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/core.h | 2 ++ 2 files changed, 77 insertions(+) (limited to 'include/linux') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f7284b905eb3..5f288aeeb721 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -448,6 +449,80 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width) mmc_set_ios(host); } +/** + * mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number + * @vdd: voltage (mV) + * @low_bits: prefer low bits in boundary cases + * + * This function returns the OCR bit number according to the provided @vdd + * value. If conversion is not possible a negative errno value returned. + * + * Depending on the @low_bits flag the function prefers low or high OCR bits + * on boundary voltages. For example, + * with @low_bits = true, 3300 mV translates to ilog2(MMC_VDD_32_33); + * with @low_bits = false, 3300 mV translates to ilog2(MMC_VDD_33_34); + * + * Any value in the [1951:1999] range translates to the ilog2(MMC_VDD_20_21). + */ +static int mmc_vdd_to_ocrbitnum(int vdd, bool low_bits) +{ + const int max_bit = ilog2(MMC_VDD_35_36); + int bit; + + if (vdd < 1650 || vdd > 3600) + return -EINVAL; + + if (vdd >= 1650 && vdd <= 1950) + return ilog2(MMC_VDD_165_195); + + if (low_bits) + vdd -= 1; + + /* Base 2000 mV, step 100 mV, bit's base 8. */ + bit = (vdd - 2000) / 100 + 8; + if (bit > max_bit) + return max_bit; + return bit; +} + +/** + * mmc_vddrange_to_ocrmask - Convert a voltage range to the OCR mask + * @vdd_min: minimum voltage value (mV) + * @vdd_max: maximum voltage value (mV) + * + * This function returns the OCR mask bits according to the provided @vdd_min + * and @vdd_max values. If conversion is not possible the function returns 0. + * + * Notes wrt boundary cases: + * This function sets the OCR bits for all boundary voltages, for example + * [3300:3400] range is translated to MMC_VDD_32_33 | MMC_VDD_33_34 | + * MMC_VDD_34_35 mask. + */ +u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max) +{ + u32 mask = 0; + + if (vdd_max < vdd_min) + return 0; + + /* Prefer high bits for the boundary vdd_max values. */ + vdd_max = mmc_vdd_to_ocrbitnum(vdd_max, false); + if (vdd_max < 0) + return 0; + + /* Prefer low bits for the boundary vdd_min values. */ + vdd_min = mmc_vdd_to_ocrbitnum(vdd_min, true); + if (vdd_min < 0) + return 0; + + /* Fill the mask, from max bit to min bit. */ + while (vdd_max >= vdd_min) + mask |= 1 << vdd_max--; + + return mask; +} +EXPORT_SYMBOL(mmc_vddrange_to_ocrmask); + /* * Mask off any voltages we don't support and select * the lowest voltage diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 143cebf0586f..7ac8b500d55c 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -151,4 +151,6 @@ static inline void mmc_claim_host(struct mmc_host *host) __mmc_claim_host(host, NULL); } +extern u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max); + #endif -- cgit v1.2.3 From 9c43df57910bbba540a6cb5c9132302a9ea5f41a Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 30 Dec 2008 18:15:28 +0300 Subject: mmc_spi: Add support for OpenFirmware bindings The support is implemented via platform data accessors, new module (of_mmc_spi) will be created automatically when the driver compiles on OpenFirmware platforms. Link-time dependency will load the module automatically. Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/Makefile | 3 + drivers/mmc/host/mmc_spi.c | 4 +- drivers/mmc/host/of_mmc_spi.c | 149 ++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/mmc_spi.h | 15 ++++- 4 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 drivers/mmc/host/of_mmc_spi.c (limited to 'include/linux') diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index c794cc5ce442..f4853288bbb1 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -19,6 +19,9 @@ obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o +ifeq ($(CONFIG_OF),y) +obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o +endif obj-$(CONFIG_MMC_S3C) += s3cmci.o obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index ad00e1632317..87e211df68ac 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1285,7 +1285,7 @@ static int mmc_spi_probe(struct spi_device *spi) /* Platform data is used to hook up things like card sensing * and power switching gpios. */ - host->pdata = spi->dev.platform_data; + host->pdata = mmc_spi_get_pdata(spi); if (host->pdata) mmc->ocr_avail = host->pdata->ocr_mask; if (!mmc->ocr_avail) { @@ -1368,6 +1368,7 @@ fail_glue_init: fail_nobuf1: mmc_free_host(mmc); + mmc_spi_put_pdata(spi); dev_set_drvdata(&spi->dev, NULL); nomem: @@ -1402,6 +1403,7 @@ static int __devexit mmc_spi_remove(struct spi_device *spi) spi->max_speed_hz = mmc->f_max; mmc_free_host(mmc); + mmc_spi_put_pdata(spi); dev_set_drvdata(&spi->dev, NULL); } return 0; diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c new file mode 100644 index 000000000000..fb2921f8099d --- /dev/null +++ b/drivers/mmc/host/of_mmc_spi.c @@ -0,0 +1,149 @@ +/* + * OpenFirmware bindings for the MMC-over-SPI driver + * + * Copyright (c) MontaVista Software, Inc. 2008. + * + * Author: Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + CD_GPIO = 0, + WP_GPIO, + NUM_GPIOS, +}; + +struct of_mmc_spi { + int gpios[NUM_GPIOS]; + bool alow_gpios[NUM_GPIOS]; + struct mmc_spi_platform_data pdata; +}; + +static struct of_mmc_spi *to_of_mmc_spi(struct device *dev) +{ + return container_of(dev->platform_data, struct of_mmc_spi, pdata); +} + +static int of_mmc_spi_read_gpio(struct device *dev, int gpio_num) +{ + struct of_mmc_spi *oms = to_of_mmc_spi(dev); + bool active_low = oms->alow_gpios[gpio_num]; + bool value = gpio_get_value(oms->gpios[gpio_num]); + + return active_low ^ value; +} + +static int of_mmc_spi_get_cd(struct device *dev) +{ + return of_mmc_spi_read_gpio(dev, CD_GPIO); +} + +static int of_mmc_spi_get_ro(struct device *dev) +{ + return of_mmc_spi_read_gpio(dev, WP_GPIO); +} + +struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct device_node *np = dev_archdata_get_node(&dev->archdata); + struct of_mmc_spi *oms; + const u32 *voltage_ranges; + int num_ranges; + int i; + int ret = -EINVAL; + + if (dev->platform_data || !np) + return dev->platform_data; + + oms = kzalloc(sizeof(*oms), GFP_KERNEL); + if (!oms) + return NULL; + + voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges); + num_ranges = num_ranges / sizeof(*voltage_ranges) / 2; + if (!voltage_ranges || !num_ranges) { + dev_err(dev, "OF: voltage-ranges unspecified\n"); + goto err_ocr; + } + + for (i = 0; i < num_ranges; i++) { + const int j = i * 2; + u32 mask; + + mask = mmc_vddrange_to_ocrmask(voltage_ranges[j], + voltage_ranges[j + 1]); + if (!mask) { + ret = -EINVAL; + dev_err(dev, "OF: voltage-range #%d is invalid\n", i); + goto err_ocr; + } + oms->pdata.ocr_mask |= mask; + } + + for (i = 0; i < ARRAY_SIZE(oms->gpios); i++) { + enum of_gpio_flags gpio_flags; + + oms->gpios[i] = of_get_gpio_flags(np, i, &gpio_flags); + if (!gpio_is_valid(oms->gpios[i])) + continue; + + ret = gpio_request(oms->gpios[i], dev->bus_id); + if (ret < 0) { + oms->gpios[i] = -EINVAL; + continue; + } + + if (gpio_flags & OF_GPIO_ACTIVE_LOW) + oms->alow_gpios[i] = true; + } + + if (gpio_is_valid(oms->gpios[CD_GPIO])) + oms->pdata.get_cd = of_mmc_spi_get_cd; + if (gpio_is_valid(oms->gpios[WP_GPIO])) + oms->pdata.get_ro = of_mmc_spi_get_ro; + + /* We don't support interrupts yet, let's poll. */ + oms->pdata.caps |= MMC_CAP_NEEDS_POLL; + + dev->platform_data = &oms->pdata; + return dev->platform_data; +err_ocr: + kfree(oms); + return NULL; +} +EXPORT_SYMBOL(mmc_spi_get_pdata); + +void mmc_spi_put_pdata(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct device_node *np = dev_archdata_get_node(&dev->archdata); + struct of_mmc_spi *oms = to_of_mmc_spi(dev); + int i; + + if (!dev->platform_data || !np) + return; + + for (i = 0; i < ARRAY_SIZE(oms->gpios); i++) { + if (gpio_is_valid(oms->gpios[i])) + gpio_free(oms->gpios[i]); + } + kfree(oms); + dev->platform_data = NULL; +} +EXPORT_SYMBOL(mmc_spi_put_pdata); diff --git a/include/linux/spi/mmc_spi.h b/include/linux/spi/mmc_spi.h index a3626aedaec9..0f4eb165f254 100644 --- a/include/linux/spi/mmc_spi.h +++ b/include/linux/spi/mmc_spi.h @@ -1,9 +1,10 @@ #ifndef __LINUX_SPI_MMC_SPI_H #define __LINUX_SPI_MMC_SPI_H +#include +#include #include -struct device; struct mmc_host; /* Put this in platform_data of a device being used to manage an MMC/SD @@ -41,4 +42,16 @@ struct mmc_spi_platform_data { void (*setpower)(struct device *, unsigned int maskval); }; +#ifdef CONFIG_OF +extern struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi); +extern void mmc_spi_put_pdata(struct spi_device *spi); +#else +static inline struct mmc_spi_platform_data * +mmc_spi_get_pdata(struct spi_device *spi) +{ + return spi->dev.platform_data; +} +static inline void mmc_spi_put_pdata(struct spi_device *spi) {} +#endif /* CONFIG_OF */ + #endif /* __LINUX_SPI_MMC_SPI_H */ -- cgit v1.2.3 From ab53d472e785e51fdfc08fc1d66252c1153e6c0f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 1 Jan 2009 10:12:19 +1030 Subject: bitmap: find_last_bit() Impact: New API As the name suggests. For the moment everyone uses the generic one. Signed-off-by: Rusty Russell --- include/linux/bitops.h | 13 ++++++++++++- lib/Kconfig | 4 ++++ lib/Makefile | 1 + lib/find_last_bit.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 lib/find_last_bit.c (limited to 'include/linux') diff --git a/include/linux/bitops.h b/include/linux/bitops.h index 024f2b027244..61829139795a 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -134,9 +134,20 @@ extern unsigned long find_first_bit(const unsigned long *addr, */ extern unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size); - #endif /* CONFIG_GENERIC_FIND_FIRST_BIT */ +#ifdef CONFIG_GENERIC_FIND_LAST_BIT +/** + * find_last_bit - find the last set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first set bit, or size. + */ +extern unsigned long find_last_bit(const unsigned long *addr, + unsigned long size); +#endif /* CONFIG_GENERIC_FIND_LAST_BIT */ + #ifdef CONFIG_GENERIC_FIND_NEXT_BIT /** diff --git a/lib/Kconfig b/lib/Kconfig index 2ba43c4a5b07..fc5f5ee50bc2 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -13,6 +13,10 @@ config GENERIC_FIND_FIRST_BIT config GENERIC_FIND_NEXT_BIT bool +config GENERIC_FIND_LAST_BIT + bool + default y + config CRC_CCITT tristate "CRC-CCITT functions" help diff --git a/lib/Makefile b/lib/Makefile index 80fe8a3ec12a..32b0e64ded27 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -37,6 +37,7 @@ lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o lib-$(CONFIG_GENERIC_FIND_FIRST_BIT) += find_next_bit.o lib-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o +lib-$(CONFIG_GENERIC_FIND_LAST_BIT) += find_last_bit.o obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o obj-$(CONFIG_LOCK_KERNEL) += kernel_lock.o obj-$(CONFIG_PLIST) += plist.o diff --git a/lib/find_last_bit.c b/lib/find_last_bit.c new file mode 100644 index 000000000000..5d202e36bdd8 --- /dev/null +++ b/lib/find_last_bit.c @@ -0,0 +1,45 @@ +/* find_last_bit.c: fallback find next bit implementation + * + * Copyright (C) 2008 IBM Corporation + * Written by Rusty Russell + * (Inspired by David Howell's find_next_bit implementation) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +unsigned long find_last_bit(const unsigned long *addr, unsigned long size) +{ + unsigned long words; + unsigned long tmp; + + /* Start at final word. */ + words = size / BITS_PER_LONG; + + /* Partial final word? */ + if (size & (BITS_PER_LONG-1)) { + tmp = (addr[words] & (~0UL >> (BITS_PER_LONG + - (size & (BITS_PER_LONG-1))))); + if (tmp) + goto found; + } + + while (words) { + tmp = addr[--words]; + if (tmp) { +found: + return words * BITS_PER_LONG + __fls(tmp); + } + } + + /* Not found */ + return size; +} +EXPORT_SYMBOL(find_last_bit); -- cgit v1.2.3 From 6b954823c24f04ed026a8517f6bab5abda279db8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 1 Jan 2009 10:12:25 +1030 Subject: cpumask: convert kernel time functions Impact: Use new APIs Convert kernel/time functions to use struct cpumask *. Note the ugly bitmap declarations in tick-broadcast.c. These should be cpumask_var_t, but there was no obvious initialization function to put the alloc_cpumask_var() calls in. This was safe. (Eventually 'struct cpumask' will be undefined for CONFIG_CPUMASK_OFFSTACK, so we use a bitmap here to show we really mean it). Signed-off-by: Rusty Russell Signed-off-by: Mike Travis --- include/linux/tick.h | 4 +- kernel/time/clocksource.c | 6 +-- kernel/time/tick-broadcast.c | 113 ++++++++++++++++++++++--------------------- kernel/time/tick-common.c | 6 +-- 4 files changed, 66 insertions(+), 63 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tick.h b/include/linux/tick.h index b6ec8189ac0c..469b82d88b3b 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -84,10 +84,10 @@ static inline void tick_cancel_sched_timer(int cpu) { } # ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST extern struct tick_device *tick_get_broadcast_device(void); -extern cpumask_t *tick_get_broadcast_mask(void); +extern struct cpumask *tick_get_broadcast_mask(void); # ifdef CONFIG_TICK_ONESHOT -extern cpumask_t *tick_get_broadcast_oneshot_mask(void); +extern struct cpumask *tick_get_broadcast_oneshot_mask(void); # endif # endif /* BROADCAST */ diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 9ed2eec97526..32141b15d63e 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -148,7 +148,7 @@ static void clocksource_watchdog(unsigned long data) int next_cpu = next_cpu_nr(raw_smp_processor_id(), cpu_online_map); if (next_cpu >= nr_cpu_ids) - next_cpu = first_cpu(cpu_online_map); + next_cpu = cpumask_first(cpu_online_mask); watchdog_timer.expires += WATCHDOG_INTERVAL; add_timer_on(&watchdog_timer, next_cpu); } @@ -173,7 +173,7 @@ static void clocksource_check_watchdog(struct clocksource *cs) watchdog_last = watchdog->read(); watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL; add_timer_on(&watchdog_timer, - first_cpu(cpu_online_map)); + cpumask_first(cpu_online_mask)); } } else { if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) @@ -195,7 +195,7 @@ static void clocksource_check_watchdog(struct clocksource *cs) watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL; add_timer_on(&watchdog_timer, - first_cpu(cpu_online_map)); + cpumask_first(cpu_online_mask)); } } } diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 9590af2327be..356fac57a182 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -28,7 +28,9 @@ */ struct tick_device tick_broadcast_device; -static cpumask_t tick_broadcast_mask; +/* FIXME: Use cpumask_var_t. */ +static DECLARE_BITMAP(tick_broadcast_mask, NR_CPUS); +static DECLARE_BITMAP(tmpmask, NR_CPUS); static DEFINE_SPINLOCK(tick_broadcast_lock); static int tick_broadcast_force; @@ -46,9 +48,9 @@ struct tick_device *tick_get_broadcast_device(void) return &tick_broadcast_device; } -cpumask_t *tick_get_broadcast_mask(void) +struct cpumask *tick_get_broadcast_mask(void) { - return &tick_broadcast_mask; + return to_cpumask(tick_broadcast_mask); } /* @@ -72,7 +74,7 @@ int tick_check_broadcast_device(struct clock_event_device *dev) clockevents_exchange_device(NULL, dev); tick_broadcast_device.evtdev = dev; - if (!cpus_empty(tick_broadcast_mask)) + if (!cpumask_empty(tick_get_broadcast_mask())) tick_broadcast_start_periodic(dev); return 1; } @@ -104,7 +106,7 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu) */ if (!tick_device_is_functional(dev)) { dev->event_handler = tick_handle_periodic; - cpu_set(cpu, tick_broadcast_mask); + cpumask_set_cpu(cpu, tick_get_broadcast_mask()); tick_broadcast_start_periodic(tick_broadcast_device.evtdev); ret = 1; } else { @@ -116,7 +118,7 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu) if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) { int cpu = smp_processor_id(); - cpu_clear(cpu, tick_broadcast_mask); + cpumask_clear_cpu(cpu, tick_get_broadcast_mask()); tick_broadcast_clear_oneshot(cpu); } } @@ -125,9 +127,9 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu) } /* - * Broadcast the event to the cpus, which are set in the mask + * Broadcast the event to the cpus, which are set in the mask (mangled). */ -static void tick_do_broadcast(cpumask_t mask) +static void tick_do_broadcast(struct cpumask *mask) { int cpu = smp_processor_id(); struct tick_device *td; @@ -135,22 +137,21 @@ static void tick_do_broadcast(cpumask_t mask) /* * Check, if the current cpu is in the mask */ - if (cpu_isset(cpu, mask)) { - cpu_clear(cpu, mask); + if (cpumask_test_cpu(cpu, mask)) { + cpumask_clear_cpu(cpu, mask); td = &per_cpu(tick_cpu_device, cpu); td->evtdev->event_handler(td->evtdev); } - if (!cpus_empty(mask)) { + if (!cpumask_empty(mask)) { /* * It might be necessary to actually check whether the devices * have different broadcast functions. For now, just use the * one of the first device. This works as long as we have this * misfeature only on x86 (lapic) */ - cpu = first_cpu(mask); - td = &per_cpu(tick_cpu_device, cpu); - td->evtdev->broadcast(&mask); + td = &per_cpu(tick_cpu_device, cpumask_first(mask)); + td->evtdev->broadcast(mask); } } @@ -160,12 +161,11 @@ static void tick_do_broadcast(cpumask_t mask) */ static void tick_do_periodic_broadcast(void) { - cpumask_t mask; - spin_lock(&tick_broadcast_lock); - cpus_and(mask, cpu_online_map, tick_broadcast_mask); - tick_do_broadcast(mask); + cpumask_and(to_cpumask(tmpmask), + cpu_online_mask, tick_get_broadcast_mask()); + tick_do_broadcast(to_cpumask(tmpmask)); spin_unlock(&tick_broadcast_lock); } @@ -228,13 +228,13 @@ static void tick_do_broadcast_on_off(void *why) if (!tick_device_is_functional(dev)) goto out; - bc_stopped = cpus_empty(tick_broadcast_mask); + bc_stopped = cpumask_empty(tick_get_broadcast_mask()); switch (*reason) { case CLOCK_EVT_NOTIFY_BROADCAST_ON: case CLOCK_EVT_NOTIFY_BROADCAST_FORCE: - if (!cpu_isset(cpu, tick_broadcast_mask)) { - cpu_set(cpu, tick_broadcast_mask); + if (!cpumask_test_cpu(cpu, tick_get_broadcast_mask())) { + cpumask_set_cpu(cpu, tick_get_broadcast_mask()); if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) clockevents_shutdown(dev); @@ -244,8 +244,8 @@ static void tick_do_broadcast_on_off(void *why) break; case CLOCK_EVT_NOTIFY_BROADCAST_OFF: if (!tick_broadcast_force && - cpu_isset(cpu, tick_broadcast_mask)) { - cpu_clear(cpu, tick_broadcast_mask); + cpumask_test_cpu(cpu, tick_get_broadcast_mask())) { + cpumask_clear_cpu(cpu, tick_get_broadcast_mask()); if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) tick_setup_periodic(dev, 0); @@ -253,7 +253,7 @@ static void tick_do_broadcast_on_off(void *why) break; } - if (cpus_empty(tick_broadcast_mask)) { + if (cpumask_empty(tick_get_broadcast_mask())) { if (!bc_stopped) clockevents_shutdown(bc); } else if (bc_stopped) { @@ -272,7 +272,7 @@ out: */ void tick_broadcast_on_off(unsigned long reason, int *oncpu) { - if (!cpu_isset(*oncpu, cpu_online_map)) + if (!cpumask_test_cpu(*oncpu, cpu_online_mask)) printk(KERN_ERR "tick-broadcast: ignoring broadcast for " "offline CPU #%d\n", *oncpu); else @@ -303,10 +303,10 @@ void tick_shutdown_broadcast(unsigned int *cpup) spin_lock_irqsave(&tick_broadcast_lock, flags); bc = tick_broadcast_device.evtdev; - cpu_clear(cpu, tick_broadcast_mask); + cpumask_clear_cpu(cpu, tick_get_broadcast_mask()); if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) { - if (bc && cpus_empty(tick_broadcast_mask)) + if (bc && cpumask_empty(tick_get_broadcast_mask())) clockevents_shutdown(bc); } @@ -342,10 +342,10 @@ int tick_resume_broadcast(void) switch (tick_broadcast_device.mode) { case TICKDEV_MODE_PERIODIC: - if(!cpus_empty(tick_broadcast_mask)) + if (!cpumask_empty(tick_get_broadcast_mask())) tick_broadcast_start_periodic(bc); - broadcast = cpu_isset(smp_processor_id(), - tick_broadcast_mask); + broadcast = cpumask_test_cpu(smp_processor_id(), + tick_get_broadcast_mask()); break; case TICKDEV_MODE_ONESHOT: broadcast = tick_resume_broadcast_oneshot(bc); @@ -360,14 +360,15 @@ int tick_resume_broadcast(void) #ifdef CONFIG_TICK_ONESHOT -static cpumask_t tick_broadcast_oneshot_mask; +/* FIXME: use cpumask_var_t. */ +static DECLARE_BITMAP(tick_broadcast_oneshot_mask, NR_CPUS); /* - * Debugging: see timer_list.c + * Exposed for debugging: see timer_list.c */ -cpumask_t *tick_get_broadcast_oneshot_mask(void) +struct cpumask *tick_get_broadcast_oneshot_mask(void) { - return &tick_broadcast_oneshot_mask; + return to_cpumask(tick_broadcast_oneshot_mask); } static int tick_broadcast_set_event(ktime_t expires, int force) @@ -389,7 +390,7 @@ int tick_resume_broadcast_oneshot(struct clock_event_device *bc) */ void tick_check_oneshot_broadcast(int cpu) { - if (cpu_isset(cpu, tick_broadcast_oneshot_mask)) { + if (cpumask_test_cpu(cpu, to_cpumask(tick_broadcast_oneshot_mask))) { struct tick_device *td = &per_cpu(tick_cpu_device, cpu); clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_ONESHOT); @@ -402,7 +403,6 @@ void tick_check_oneshot_broadcast(int cpu) static void tick_handle_oneshot_broadcast(struct clock_event_device *dev) { struct tick_device *td; - cpumask_t mask; ktime_t now, next_event; int cpu; @@ -410,13 +410,13 @@ static void tick_handle_oneshot_broadcast(struct clock_event_device *dev) again: dev->next_event.tv64 = KTIME_MAX; next_event.tv64 = KTIME_MAX; - mask = CPU_MASK_NONE; + cpumask_clear(to_cpumask(tmpmask)); now = ktime_get(); /* Find all expired events */ - for_each_cpu_mask_nr(cpu, tick_broadcast_oneshot_mask) { + for_each_cpu(cpu, tick_get_broadcast_oneshot_mask()) { td = &per_cpu(tick_cpu_device, cpu); if (td->evtdev->next_event.tv64 <= now.tv64) - cpu_set(cpu, mask); + cpumask_set_cpu(cpu, to_cpumask(tmpmask)); else if (td->evtdev->next_event.tv64 < next_event.tv64) next_event.tv64 = td->evtdev->next_event.tv64; } @@ -424,7 +424,7 @@ again: /* * Wakeup the cpus which have an expired event. */ - tick_do_broadcast(mask); + tick_do_broadcast(to_cpumask(tmpmask)); /* * Two reasons for reprogram: @@ -476,15 +476,16 @@ void tick_broadcast_oneshot_control(unsigned long reason) goto out; if (reason == CLOCK_EVT_NOTIFY_BROADCAST_ENTER) { - if (!cpu_isset(cpu, tick_broadcast_oneshot_mask)) { - cpu_set(cpu, tick_broadcast_oneshot_mask); + if (!cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) { + cpumask_set_cpu(cpu, tick_get_broadcast_oneshot_mask()); clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN); if (dev->next_event.tv64 < bc->next_event.tv64) tick_broadcast_set_event(dev->next_event, 1); } } else { - if (cpu_isset(cpu, tick_broadcast_oneshot_mask)) { - cpu_clear(cpu, tick_broadcast_oneshot_mask); + if (cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) { + cpumask_clear_cpu(cpu, + tick_get_broadcast_oneshot_mask()); clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); if (dev->next_event.tv64 != KTIME_MAX) tick_program_event(dev->next_event, 1); @@ -502,10 +503,11 @@ out: */ static void tick_broadcast_clear_oneshot(int cpu) { - cpu_clear(cpu, tick_broadcast_oneshot_mask); + cpumask_clear_cpu(cpu, tick_get_broadcast_oneshot_mask()); } -static void tick_broadcast_init_next_event(cpumask_t *mask, ktime_t expires) +static void tick_broadcast_init_next_event(struct cpumask *mask, + ktime_t expires) { struct tick_device *td; int cpu; @@ -526,7 +528,6 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) if (bc->event_handler != tick_handle_oneshot_broadcast) { int was_periodic = bc->mode == CLOCK_EVT_MODE_PERIODIC; int cpu = smp_processor_id(); - cpumask_t mask; bc->event_handler = tick_handle_oneshot_broadcast; clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); @@ -540,13 +541,15 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) * oneshot_mask bits for those and program the * broadcast device to fire. */ - mask = tick_broadcast_mask; - cpu_clear(cpu, mask); - cpus_or(tick_broadcast_oneshot_mask, - tick_broadcast_oneshot_mask, mask); - - if (was_periodic && !cpus_empty(mask)) { - tick_broadcast_init_next_event(&mask, tick_next_period); + cpumask_copy(to_cpumask(tmpmask), tick_get_broadcast_mask()); + cpumask_clear_cpu(cpu, to_cpumask(tmpmask)); + cpumask_or(tick_get_broadcast_oneshot_mask(), + tick_get_broadcast_oneshot_mask(), + to_cpumask(tmpmask)); + + if (was_periodic && !cpumask_empty(to_cpumask(tmpmask))) { + tick_broadcast_init_next_event(to_cpumask(tmpmask), + tick_next_period); tick_broadcast_set_event(tick_next_period, 1); } else bc->next_event.tv64 = KTIME_MAX; @@ -585,7 +588,7 @@ void tick_shutdown_broadcast_oneshot(unsigned int *cpup) * Clear the broadcast mask flag for the dead cpu, but do not * stop the broadcast device! */ - cpu_clear(cpu, tick_broadcast_oneshot_mask); + cpumask_clear_cpu(cpu, tick_get_broadcast_oneshot_mask()); spin_unlock_irqrestore(&tick_broadcast_lock, flags); } diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index f8372be74122..63e05d423a09 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -254,7 +254,7 @@ static int tick_check_new_device(struct clock_event_device *newdev) curdev = NULL; } clockevents_exchange_device(curdev, newdev); - tick_setup_device(td, newdev, cpu, &cpumask_of_cpu(cpu)); + tick_setup_device(td, newdev, cpu, cpumask_of(cpu)); if (newdev->features & CLOCK_EVT_FEAT_ONESHOT) tick_oneshot_notify(); @@ -299,9 +299,9 @@ static void tick_shutdown(unsigned int *cpup) } /* Transfer the do_timer job away from this cpu */ if (*cpup == tick_do_timer_cpu) { - int cpu = first_cpu(cpu_online_map); + int cpu = cpumask_first(cpu_online_mask); - tick_do_timer_cpu = (cpu != NR_CPUS) ? cpu : + tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu : TICK_DO_TIMER_NONE; } spin_unlock_irqrestore(&tick_device_lock, flags); -- cgit v1.2.3 From d036e67b40f52bdd95392390108defbac7e53837 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 1 Jan 2009 10:12:26 +1030 Subject: cpumask: convert kernel/irq Impact: Reduce stack usage, use new cpumask API. ALPHA mod! Main change is that irq_default_affinity becomes a cpumask_var_t, so treat it as a pointer (this effects alpha). Signed-off-by: Rusty Russell --- arch/alpha/kernel/irq.c | 3 ++- include/linux/interrupt.h | 2 +- kernel/irq/manage.c | 11 +++++++++-- kernel/irq/proc.c | 32 +++++++++++++++++++++----------- 4 files changed, 33 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index d0f1620007f7..703731accda6 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -50,7 +50,8 @@ int irq_select_affinity(unsigned int irq) if (!irq_desc[irq].chip->set_affinity || irq_user_affinity[irq]) return 1; - while (!cpu_possible(cpu) || !cpu_isset(cpu, irq_default_affinity)) + while (!cpu_possible(cpu) || + !cpumask_test_cpu(cpu, irq_default_affinity)) cpu = (cpu < (NR_CPUS-1) ? cpu + 1 : 0); last_cpu = cpu; diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index dfaee6bd265b..91f1ef8e5810 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -109,7 +109,7 @@ extern void enable_irq(unsigned int irq); #if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS) -extern cpumask_t irq_default_affinity; +extern cpumask_var_t irq_default_affinity; extern int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask); extern int irq_can_set_affinity(unsigned int irq); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 61c4a9b62165..cd0cd8dcb345 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -16,8 +16,15 @@ #include "internals.h" #ifdef CONFIG_SMP +cpumask_var_t irq_default_affinity; -cpumask_t irq_default_affinity = CPU_MASK_ALL; +static int init_irq_default_affinity(void) +{ + alloc_cpumask_var(&irq_default_affinity, GFP_KERNEL); + cpumask_setall(irq_default_affinity); + return 0; +} +core_initcall(init_irq_default_affinity); /** * synchronize_irq - wait for pending IRQ handlers (on other CPUs) @@ -127,7 +134,7 @@ int do_irq_select_affinity(unsigned int irq, struct irq_desc *desc) desc->status &= ~IRQ_AFFINITY_SET; } - cpumask_and(&desc->affinity, cpu_online_mask, &irq_default_affinity); + cpumask_and(&desc->affinity, cpu_online_mask, irq_default_affinity); set_affinity: desc->chip->set_affinity(irq, &desc->affinity); diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index d2c0e5ee53c5..2abd3a7716ed 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -20,7 +20,7 @@ static struct proc_dir_entry *root_irq_dir; static int irq_affinity_proc_show(struct seq_file *m, void *v) { struct irq_desc *desc = irq_to_desc((long)m->private); - cpumask_t *mask = &desc->affinity; + const struct cpumask *mask = &desc->affinity; #ifdef CONFIG_GENERIC_PENDING_IRQ if (desc->status & IRQ_MOVE_PENDING) @@ -93,7 +93,7 @@ static const struct file_operations irq_affinity_proc_fops = { static int default_affinity_show(struct seq_file *m, void *v) { - seq_cpumask(m, &irq_default_affinity); + seq_cpumask(m, irq_default_affinity); seq_putc(m, '\n'); return 0; } @@ -101,27 +101,37 @@ static int default_affinity_show(struct seq_file *m, void *v) static ssize_t default_affinity_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - cpumask_t new_value; + cpumask_var_t new_value; int err; - err = cpumask_parse_user(buffer, count, &new_value); + if (!alloc_cpumask_var(&new_value, GFP_KERNEL)) + return -ENOMEM; + + err = cpumask_parse_user(buffer, count, new_value); if (err) - return err; + goto out; - if (!is_affinity_mask_valid(new_value)) - return -EINVAL; + if (!is_affinity_mask_valid(new_value)) { + err = -EINVAL; + goto out; + } /* * Do not allow disabling IRQs completely - it's a too easy * way to make the system unusable accidentally :-) At least * one online CPU still has to be targeted. */ - if (!cpus_intersects(new_value, cpu_online_map)) - return -EINVAL; + if (!cpumask_intersects(new_value, cpu_online_mask)) { + err = -EINVAL; + goto out; + } - irq_default_affinity = new_value; + cpumask_copy(irq_default_affinity, new_value); + err = count; - return count; +out: + free_cpumask_var(new_value); + return err; } static int default_affinity_open(struct inode *inode, struct file *file) -- cgit v1.2.3 From bd232f97b30f6bb630efa136a777647545db3039 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 1 Jan 2009 10:12:26 +1030 Subject: cpumask: convert RCU implementations Impact: use new cpumask API. rcu_ctrlblk contains a cpumask, and it's highly optimized so I don't want a cpumask_var_t (ie. a pointer) for the CONFIG_CPUMASK_OFFSTACK case. It could use a dangling bitmap, and be allocated in __rcu_init to save memory, but for the moment we use a bitmap. (Eventually 'struct cpumask' will be undefined for CONFIG_CPUMASK_OFFSTACK, so we use a bitmap here to show we really mean it). We remove on-stack cpumasks, using cpumask_var_t for rcu_torture_shuffle_tasks() and for_each_cpu_and in force_quiescent_state(). Signed-off-by: Rusty Russell --- include/linux/rcuclassic.h | 4 ++-- kernel/rcuclassic.c | 32 +++++++++++++++++--------------- kernel/rcupreempt.c | 19 ++++++++++--------- kernel/rcutorture.c | 27 +++++++++++++++------------ 4 files changed, 44 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h index 301dda829e37..f3f697df1d71 100644 --- a/include/linux/rcuclassic.h +++ b/include/linux/rcuclassic.h @@ -59,8 +59,8 @@ struct rcu_ctrlblk { int signaled; spinlock_t lock ____cacheline_internodealigned_in_smp; - cpumask_t cpumask; /* CPUs that need to switch in order */ - /* for current batch to proceed. */ + DECLARE_BITMAP(cpumask, NR_CPUS); /* CPUs that need to switch for */ + /* current batch to proceed. */ } ____cacheline_internodealigned_in_smp; /* Is batch a before batch b ? */ diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c index e503a002f330..0ff9b05706a6 100644 --- a/kernel/rcuclassic.c +++ b/kernel/rcuclassic.c @@ -63,14 +63,14 @@ static struct rcu_ctrlblk rcu_ctrlblk = { .completed = -300, .pending = -300, .lock = __SPIN_LOCK_UNLOCKED(&rcu_ctrlblk.lock), - .cpumask = CPU_MASK_NONE, + .cpumask = CPU_BITS_NONE, }; static struct rcu_ctrlblk rcu_bh_ctrlblk = { .cur = -300, .completed = -300, .pending = -300, .lock = __SPIN_LOCK_UNLOCKED(&rcu_bh_ctrlblk.lock), - .cpumask = CPU_MASK_NONE, + .cpumask = CPU_BITS_NONE, }; DEFINE_PER_CPU(struct rcu_data, rcu_data) = { 0L }; @@ -85,7 +85,6 @@ static void force_quiescent_state(struct rcu_data *rdp, struct rcu_ctrlblk *rcp) { int cpu; - cpumask_t cpumask; unsigned long flags; set_need_resched(); @@ -96,10 +95,10 @@ static void force_quiescent_state(struct rcu_data *rdp, * Don't send IPI to itself. With irqs disabled, * rdp->cpu is the current cpu. * - * cpu_online_map is updated by the _cpu_down() + * cpu_online_mask is updated by the _cpu_down() * using __stop_machine(). Since we're in irqs disabled * section, __stop_machine() is not exectuting, hence - * the cpu_online_map is stable. + * the cpu_online_mask is stable. * * However, a cpu might have been offlined _just_ before * we disabled irqs while entering here. @@ -107,13 +106,14 @@ static void force_quiescent_state(struct rcu_data *rdp, * notification, leading to the offlined cpu's bit * being set in the rcp->cpumask. * - * Hence cpumask = (rcp->cpumask & cpu_online_map) to prevent + * Hence cpumask = (rcp->cpumask & cpu_online_mask) to prevent * sending smp_reschedule() to an offlined CPU. */ - cpus_and(cpumask, rcp->cpumask, cpu_online_map); - cpu_clear(rdp->cpu, cpumask); - for_each_cpu_mask_nr(cpu, cpumask) - smp_send_reschedule(cpu); + for_each_cpu_and(cpu, + to_cpumask(rcp->cpumask), cpu_online_mask) { + if (cpu != rdp->cpu) + smp_send_reschedule(cpu); + } } spin_unlock_irqrestore(&rcp->lock, flags); } @@ -193,7 +193,7 @@ static void print_other_cpu_stall(struct rcu_ctrlblk *rcp) printk(KERN_ERR "INFO: RCU detected CPU stalls:"); for_each_possible_cpu(cpu) { - if (cpu_isset(cpu, rcp->cpumask)) + if (cpumask_test_cpu(cpu, to_cpumask(rcp->cpumask))) printk(" %d", cpu); } printk(" (detected by %d, t=%ld jiffies)\n", @@ -221,7 +221,8 @@ static void check_cpu_stall(struct rcu_ctrlblk *rcp) long delta; delta = jiffies - rcp->jiffies_stall; - if (cpu_isset(smp_processor_id(), rcp->cpumask) && delta >= 0) { + if (cpumask_test_cpu(smp_processor_id(), to_cpumask(rcp->cpumask)) && + delta >= 0) { /* We haven't checked in, so go dump stack. */ print_cpu_stall(rcp); @@ -393,7 +394,8 @@ static void rcu_start_batch(struct rcu_ctrlblk *rcp) * unnecessarily. */ smp_mb(); - cpus_andnot(rcp->cpumask, cpu_online_map, nohz_cpu_mask); + cpumask_andnot(to_cpumask(rcp->cpumask), + cpu_online_mask, &nohz_cpu_mask); rcp->signaled = 0; } @@ -406,8 +408,8 @@ static void rcu_start_batch(struct rcu_ctrlblk *rcp) */ static void cpu_quiet(int cpu, struct rcu_ctrlblk *rcp) { - cpu_clear(cpu, rcp->cpumask); - if (cpus_empty(rcp->cpumask)) { + cpumask_clear_cpu(cpu, to_cpumask(rcp->cpumask)); + if (cpumask_empty(to_cpumask(rcp->cpumask))) { /* batch completed ! */ rcp->completed = rcp->cur; rcu_start_batch(rcp); diff --git a/kernel/rcupreempt.c b/kernel/rcupreempt.c index 04982659875a..f9dc8f3720f6 100644 --- a/kernel/rcupreempt.c +++ b/kernel/rcupreempt.c @@ -164,7 +164,8 @@ static char *rcu_try_flip_state_names[] = { "idle", "waitack", "waitzero", "waitmb" }; #endif /* #ifdef CONFIG_RCU_TRACE */ -static cpumask_t rcu_cpu_online_map __read_mostly = CPU_MASK_NONE; +static DECLARE_BITMAP(rcu_cpu_online_map, NR_CPUS) __read_mostly + = CPU_BITS_NONE; /* * Enum and per-CPU flag to determine when each CPU has seen @@ -758,7 +759,7 @@ rcu_try_flip_idle(void) /* Now ask each CPU for acknowledgement of the flip. */ - for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) { + for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map)) { per_cpu(rcu_flip_flag, cpu) = rcu_flipped; dyntick_save_progress_counter(cpu); } @@ -776,7 +777,7 @@ rcu_try_flip_waitack(void) int cpu; RCU_TRACE_ME(rcupreempt_trace_try_flip_a1); - for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) + for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map)) if (rcu_try_flip_waitack_needed(cpu) && per_cpu(rcu_flip_flag, cpu) != rcu_flip_seen) { RCU_TRACE_ME(rcupreempt_trace_try_flip_ae1); @@ -808,7 +809,7 @@ rcu_try_flip_waitzero(void) /* Check to see if the sum of the "last" counters is zero. */ RCU_TRACE_ME(rcupreempt_trace_try_flip_z1); - for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) + for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map)) sum += RCU_DATA_CPU(cpu)->rcu_flipctr[lastidx]; if (sum != 0) { RCU_TRACE_ME(rcupreempt_trace_try_flip_ze1); @@ -823,7 +824,7 @@ rcu_try_flip_waitzero(void) smp_mb(); /* ^^^^^^^^^^^^ */ /* Call for a memory barrier from each CPU. */ - for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) { + for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map)) { per_cpu(rcu_mb_flag, cpu) = rcu_mb_needed; dyntick_save_progress_counter(cpu); } @@ -843,7 +844,7 @@ rcu_try_flip_waitmb(void) int cpu; RCU_TRACE_ME(rcupreempt_trace_try_flip_m1); - for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) + for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map)) if (rcu_try_flip_waitmb_needed(cpu) && per_cpu(rcu_mb_flag, cpu) != rcu_mb_done) { RCU_TRACE_ME(rcupreempt_trace_try_flip_me1); @@ -1032,7 +1033,7 @@ void rcu_offline_cpu(int cpu) RCU_DATA_CPU(cpu)->rcu_flipctr[0] = 0; RCU_DATA_CPU(cpu)->rcu_flipctr[1] = 0; - cpu_clear(cpu, rcu_cpu_online_map); + cpumask_clear_cpu(cpu, to_cpumask(rcu_cpu_online_map)); spin_unlock_irqrestore(&rcu_ctrlblk.fliplock, flags); @@ -1072,7 +1073,7 @@ void __cpuinit rcu_online_cpu(int cpu) struct rcu_data *rdp; spin_lock_irqsave(&rcu_ctrlblk.fliplock, flags); - cpu_set(cpu, rcu_cpu_online_map); + cpumask_set_cpu(cpu, to_cpumask(rcu_cpu_online_map)); spin_unlock_irqrestore(&rcu_ctrlblk.fliplock, flags); /* @@ -1430,7 +1431,7 @@ void __init __rcu_init(void) * We don't need protection against CPU-Hotplug here * since * a) If a CPU comes online while we are iterating over the - * cpu_online_map below, we would only end up making a + * cpu_online_mask below, we would only end up making a * duplicate call to rcu_online_cpu() which sets the corresponding * CPU's mask in the rcu_cpu_online_map. * diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index b31065522104..3245b40952c6 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c @@ -868,49 +868,52 @@ static int rcu_idle_cpu; /* Force all torture tasks off this CPU */ */ static void rcu_torture_shuffle_tasks(void) { - cpumask_t tmp_mask; + cpumask_var_t tmp_mask; int i; - cpus_setall(tmp_mask); + if (!alloc_cpumask_var(&tmp_mask, GFP_KERNEL)) + BUG(); + + cpumask_setall(tmp_mask); get_online_cpus(); /* No point in shuffling if there is only one online CPU (ex: UP) */ - if (num_online_cpus() == 1) { - put_online_cpus(); - return; - } + if (num_online_cpus() == 1) + goto out; if (rcu_idle_cpu != -1) - cpu_clear(rcu_idle_cpu, tmp_mask); + cpumask_clear_cpu(rcu_idle_cpu, tmp_mask); - set_cpus_allowed_ptr(current, &tmp_mask); + set_cpus_allowed_ptr(current, tmp_mask); if (reader_tasks) { for (i = 0; i < nrealreaders; i++) if (reader_tasks[i]) set_cpus_allowed_ptr(reader_tasks[i], - &tmp_mask); + tmp_mask); } if (fakewriter_tasks) { for (i = 0; i < nfakewriters; i++) if (fakewriter_tasks[i]) set_cpus_allowed_ptr(fakewriter_tasks[i], - &tmp_mask); + tmp_mask); } if (writer_task) - set_cpus_allowed_ptr(writer_task, &tmp_mask); + set_cpus_allowed_ptr(writer_task, tmp_mask); if (stats_task) - set_cpus_allowed_ptr(stats_task, &tmp_mask); + set_cpus_allowed_ptr(stats_task, tmp_mask); if (rcu_idle_cpu == -1) rcu_idle_cpu = num_online_cpus() - 1; else rcu_idle_cpu--; +out: put_online_cpus(); + free_cpumask_var(tmp_mask); } /* Shuffle tasks across CPUs, with the intent of allowing each CPU in the -- cgit v1.2.3 From 41c7bb9588904eb060a95bcad47bd3804a1ece25 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 1 Jan 2009 10:12:28 +1030 Subject: cpumask: convert rest of files in kernel/ Impact: Reduce stack usage, use new cpumask API. Mainly changing cpumask_t to 'struct cpumask' and similar simple API conversion. Two conversions worth mentioning: 1) we use cpumask_any_but to avoid a temporary in kernel/softlockup.c, 2) Use cpumask_var_t in taskstats_user_cmd(). Signed-off-by: Rusty Russell Signed-off-by: Mike Travis Cc: Balbir Singh Cc: Ingo Molnar --- include/linux/stop_machine.h | 6 +++--- kernel/power/poweroff.c | 2 +- kernel/softlockup.c | 6 ++---- kernel/stop_machine.c | 8 ++++---- kernel/taskstats.c | 39 ++++++++++++++++++++++++--------------- 5 files changed, 34 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index faf1519b5adc..74d59a641362 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -23,7 +23,7 @@ * * This can be thought of as a very heavy write lock, equivalent to * grabbing every spinlock in the kernel. */ -int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus); +int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus); /** * __stop_machine: freeze the machine on all CPUs and run this function @@ -34,11 +34,11 @@ int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus); * Description: This is a special version of the above, which assumes cpus * won't come or go while it's being called. Used by hotplug cpu. */ -int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus); +int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus); #else static inline int stop_machine(int (*fn)(void *), void *data, - const cpumask_t *cpus) + const struct cpumask *cpus) { int ret; local_irq_disable(); diff --git a/kernel/power/poweroff.c b/kernel/power/poweroff.c index 72016f051477..97890831e1b5 100644 --- a/kernel/power/poweroff.c +++ b/kernel/power/poweroff.c @@ -27,7 +27,7 @@ static DECLARE_WORK(poweroff_work, do_poweroff); static void handle_poweroff(int key, struct tty_struct *tty) { /* run sysrq poweroff on boot cpu */ - schedule_work_on(first_cpu(cpu_online_map), &poweroff_work); + schedule_work_on(cpumask_first(cpu_online_mask), &poweroff_work); } static struct sysrq_key_op sysrq_poweroff_op = { diff --git a/kernel/softlockup.c b/kernel/softlockup.c index 492f0c72fec5..d9188c66278a 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -310,10 +310,8 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) case CPU_DOWN_PREPARE: case CPU_DOWN_PREPARE_FROZEN: if (hotcpu == check_cpu) { - cpumask_t temp_cpu_online_map = cpu_online_map; - - cpu_clear(hotcpu, temp_cpu_online_map); - check_cpu = cpumask_any(&temp_cpu_online_map); + /* Pick any other online cpu. */ + check_cpu = cpumask_any_but(cpu_online_mask, hotcpu); } break; diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index 24e8ceacc388..286c41722e8c 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -69,10 +69,10 @@ static void stop_cpu(struct work_struct *unused) int err; if (!active_cpus) { - if (cpu == first_cpu(cpu_online_map)) + if (cpu == cpumask_first(cpu_online_mask)) smdata = &active; } else { - if (cpu_isset(cpu, *active_cpus)) + if (cpumask_test_cpu(cpu, active_cpus)) smdata = &active; } /* Simple state machine */ @@ -109,7 +109,7 @@ static int chill(void *unused) return 0; } -int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) +int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) { struct work_struct *sm_work; int i, ret; @@ -142,7 +142,7 @@ int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) return ret; } -int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) +int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) { int ret; diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 6d7dc4ec4aa5..888adbcca30c 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -290,18 +290,17 @@ ret: return; } -static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd) +static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd) { struct listener_list *listeners; struct listener *s, *tmp; unsigned int cpu; - cpumask_t mask = *maskp; - if (!cpus_subset(mask, cpu_possible_map)) + if (!cpumask_subset(mask, cpu_possible_mask)) return -EINVAL; if (isadd == REGISTER) { - for_each_cpu_mask_nr(cpu, mask) { + for_each_cpu(cpu, mask) { s = kmalloc_node(sizeof(struct listener), GFP_KERNEL, cpu_to_node(cpu)); if (!s) @@ -320,7 +319,7 @@ static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd) /* Deregister or cleanup */ cleanup: - for_each_cpu_mask_nr(cpu, mask) { + for_each_cpu(cpu, mask) { listeners = &per_cpu(listener_array, cpu); down_write(&listeners->sem); list_for_each_entry_safe(s, tmp, &listeners->list, list) { @@ -335,7 +334,7 @@ cleanup: return 0; } -static int parse(struct nlattr *na, cpumask_t *mask) +static int parse(struct nlattr *na, struct cpumask *mask) { char *data; int len; @@ -428,23 +427,33 @@ err: static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) { - int rc = 0; + int rc; struct sk_buff *rep_skb; struct taskstats *stats; size_t size; - cpumask_t mask; + cpumask_var_t mask; + + if (!alloc_cpumask_var(&mask, GFP_KERNEL)) + return -ENOMEM; - rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], &mask); + rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], mask); if (rc < 0) - return rc; - if (rc == 0) - return add_del_listener(info->snd_pid, &mask, REGISTER); + goto free_return_rc; + if (rc == 0) { + rc = add_del_listener(info->snd_pid, mask, REGISTER); + goto free_return_rc; + } - rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], &mask); + rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], mask); if (rc < 0) + goto free_return_rc; + if (rc == 0) { + rc = add_del_listener(info->snd_pid, mask, DEREGISTER); +free_return_rc: + free_cpumask_var(mask); return rc; - if (rc == 0) - return add_del_listener(info->snd_pid, &mask, DEREGISTER); + } + free_cpumask_var(mask); /* * Size includes space for nested attributes -- cgit v1.2.3 From 8c384cdee3e04d6194a2c2b192b624754f990835 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 1 Jan 2009 10:12:30 +1030 Subject: cpumask: CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS Impact: new debug CONFIG options This helps find unconverted code. It currently breaks compile horribly, but we never wanted a flag day so that's expected. Signed-off-by: Rusty Russell --- include/linux/cpumask.h | 11 ++++++++++- lib/Kconfig | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 7c178a6baae3..9f315382610b 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -144,6 +144,7 @@ typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t; extern cpumask_t _unused_cpumask_arg_; +#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS #define cpu_set(cpu, dst) __cpu_set((cpu), &(dst)) static inline void __cpu_set(int cpu, volatile cpumask_t *dstp) { @@ -267,6 +268,7 @@ static inline void __cpus_shift_left(cpumask_t *dstp, { bitmap_shift_left(dstp->bits, srcp->bits, n, nbits); } +#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */ /** * to_cpumask - convert an NR_CPUS bitmap to a struct cpumask * @@ -304,6 +306,7 @@ static inline const struct cpumask *get_cpu_mask(unsigned int cpu) return to_cpumask(p); } +#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS /* * In cases where we take the address of the cpumask immediately, * gcc optimizes it out (it's a constant) and there's no huge stack @@ -389,19 +392,22 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp, { bitmap_fold(dstp->bits, origp->bits, sz, nbits); } +#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */ #if NR_CPUS == 1 #define nr_cpu_ids 1 +#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS #define first_cpu(src) ({ (void)(src); 0; }) #define next_cpu(n, src) ({ (void)(src); 1; }) #define any_online_cpu(mask) 0 #define for_each_cpu_mask(cpu, mask) \ for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask) - +#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */ #else /* NR_CPUS > 1 */ extern int nr_cpu_ids; +#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS int __first_cpu(const cpumask_t *srcp); int __next_cpu(int n, const cpumask_t *srcp); int __any_online_cpu(const cpumask_t *mask); @@ -413,8 +419,10 @@ int __any_online_cpu(const cpumask_t *mask); for ((cpu) = -1; \ (cpu) = next_cpu((cpu), (mask)), \ (cpu) < NR_CPUS; ) +#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */ #endif +#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS #if NR_CPUS <= 64 #define next_cpu_nr(n, src) next_cpu(n, src) @@ -432,6 +440,7 @@ int __next_cpu_nr(int n, const cpumask_t *srcp); (cpu) < nr_cpu_ids; ) #endif /* NR_CPUS > 64 */ +#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */ /* * The following particular system cpumasks and operations manage diff --git a/lib/Kconfig b/lib/Kconfig index fc5f5ee50bc2..03c2c24b9083 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -170,4 +170,8 @@ config CPUMASK_OFFSTACK them on the stack. This is a bit more expensive, but avoids stack overflow. +config DISABLE_OBSOLETE_CPUMASK_FUNCTIONS + bool "Disable obsolete cpumask functions" if DEBUG_PER_CPU_MAPS + depends on EXPERIMENTAL && BROKEN + endmenu -- cgit v1.2.3 From ebdab07dad3d3a008e519b0a028e1e1ad5ecaef0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 2 Jan 2009 16:12:48 +0100 Subject: ide: move sysfs support to ide-sysfs.c While at it: - media_string() -> ide_media_string() There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Makefile | 2 +- drivers/ide/ide-probe.c | 52 -------------------- drivers/ide/ide-sysfs.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide.c | 72 +--------------------------- include/linux/ide.h | 4 ++ 5 files changed, 132 insertions(+), 123 deletions(-) create mode 100644 drivers/ide/ide-sysfs.c (limited to 'include/linux') diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 177e3f8523ed..410728992e6a 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -5,7 +5,7 @@ EXTRA_CFLAGS += -Idrivers/ide ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \ - ide-taskfile.o ide-pm.o ide-park.o ide-pio-blacklist.o + ide-taskfile.o ide-pm.o ide-park.o ide-pio-blacklist.o ide-sysfs.o # core IDE code ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 91f5faee7404..f9efd069edc2 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1420,58 +1420,6 @@ static void ide_port_cable_detect(ide_hwif_t *hwif) } } -static ssize_t store_delete_devices(struct device *portdev, - struct device_attribute *attr, - const char *buf, size_t n) -{ - ide_hwif_t *hwif = dev_get_drvdata(portdev); - - if (strncmp(buf, "1", n)) - return -EINVAL; - - ide_port_unregister_devices(hwif); - - return n; -}; - -static DEVICE_ATTR(delete_devices, S_IWUSR, NULL, store_delete_devices); - -static ssize_t store_scan(struct device *portdev, - struct device_attribute *attr, - const char *buf, size_t n) -{ - ide_hwif_t *hwif = dev_get_drvdata(portdev); - - if (strncmp(buf, "1", n)) - return -EINVAL; - - ide_port_unregister_devices(hwif); - ide_port_scan(hwif); - - return n; -}; - -static DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan); - -static struct device_attribute *ide_port_attrs[] = { - &dev_attr_delete_devices, - &dev_attr_scan, - NULL -}; - -static int ide_sysfs_register_port(ide_hwif_t *hwif) -{ - int i, uninitialized_var(rc); - - for (i = 0; ide_port_attrs[i]; i++) { - rc = device_create_file(hwif->portdev, ide_port_attrs[i]); - if (rc) - break; - } - - return rc; -} - static unsigned int ide_indexes; /** diff --git a/drivers/ide/ide-sysfs.c b/drivers/ide/ide-sysfs.c new file mode 100644 index 000000000000..883ffacaf45a --- /dev/null +++ b/drivers/ide/ide-sysfs.c @@ -0,0 +1,125 @@ +#include +#include + +char *ide_media_string(ide_drive_t *drive) +{ + switch (drive->media) { + case ide_disk: + return "disk"; + case ide_cdrom: + return "cdrom"; + case ide_tape: + return "tape"; + case ide_floppy: + return "floppy"; + case ide_optical: + return "optical"; + default: + return "UNKNOWN"; + } +} + +static ssize_t media_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "%s\n", ide_media_string(drive)); +} + +static ssize_t drivename_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "%s\n", drive->name); +} + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "ide:m-%s\n", ide_media_string(drive)); +} + +static ssize_t model_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_PROD]); +} + +static ssize_t firmware_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_FW_REV]); +} + +static ssize_t serial_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_SERNO]); +} + +struct device_attribute ide_dev_attrs[] = { + __ATTR_RO(media), + __ATTR_RO(drivename), + __ATTR_RO(modalias), + __ATTR_RO(model), + __ATTR_RO(firmware), + __ATTR(serial, 0400, serial_show, NULL), + __ATTR(unload_heads, 0644, ide_park_show, ide_park_store), + __ATTR_NULL +}; + +static ssize_t store_delete_devices(struct device *portdev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + ide_hwif_t *hwif = dev_get_drvdata(portdev); + + if (strncmp(buf, "1", n)) + return -EINVAL; + + ide_port_unregister_devices(hwif); + + return n; +}; + +static DEVICE_ATTR(delete_devices, S_IWUSR, NULL, store_delete_devices); + +static ssize_t store_scan(struct device *portdev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + ide_hwif_t *hwif = dev_get_drvdata(portdev); + + if (strncmp(buf, "1", n)) + return -EINVAL; + + ide_port_unregister_devices(hwif); + ide_port_scan(hwif); + + return n; +}; + +static DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan); + +static struct device_attribute *ide_port_attrs[] = { + &dev_attr_delete_devices, + &dev_attr_scan, + NULL +}; + +int ide_sysfs_register_port(ide_hwif_t *hwif) +{ + int i, uninitialized_var(rc); + + for (i = 0; ide_port_attrs[i]; i++) { + rc = device_create_file(hwif->portdev, ide_port_attrs[i]); + if (rc) + break; + } + + return rc; +} diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index f0f09f702e9c..46a2d4ca812b 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -440,81 +440,13 @@ static int ide_bus_match(struct device *dev, struct device_driver *drv) return 1; } -static char *media_string(ide_drive_t *drive) -{ - switch (drive->media) { - case ide_disk: - return "disk"; - case ide_cdrom: - return "cdrom"; - case ide_tape: - return "tape"; - case ide_floppy: - return "floppy"; - case ide_optical: - return "optical"; - default: - return "UNKNOWN"; - } -} - -static ssize_t media_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - ide_drive_t *drive = to_ide_device(dev); - return sprintf(buf, "%s\n", media_string(drive)); -} - -static ssize_t drivename_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - ide_drive_t *drive = to_ide_device(dev); - return sprintf(buf, "%s\n", drive->name); -} - -static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - ide_drive_t *drive = to_ide_device(dev); - return sprintf(buf, "ide:m-%s\n", media_string(drive)); -} - -static ssize_t model_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - ide_drive_t *drive = to_ide_device(dev); - return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_PROD]); -} - -static ssize_t firmware_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - ide_drive_t *drive = to_ide_device(dev); - return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_FW_REV]); -} - -static ssize_t serial_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - ide_drive_t *drive = to_ide_device(dev); - return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_SERNO]); -} - -static struct device_attribute ide_dev_attrs[] = { - __ATTR_RO(media), - __ATTR_RO(drivename), - __ATTR_RO(modalias), - __ATTR_RO(model), - __ATTR_RO(firmware), - __ATTR(serial, 0400, serial_show, NULL), - __ATTR(unload_heads, 0644, ide_park_show, ide_park_store), - __ATTR_NULL -}; - static int ide_uevent(struct device *dev, struct kobj_uevent_env *env) { ide_drive_t *drive = to_ide_device(dev); - add_uevent_var(env, "MEDIA=%s", media_string(drive)); + add_uevent_var(env, "MEDIA=%s", ide_media_string(drive)); add_uevent_var(env, "DRIVENAME=%s", drive->name); - add_uevent_var(env, "MODALIAS=ide:m-%s", media_string(drive)); + add_uevent_var(env, "MODALIAS=ide:m-%s", ide_media_string(drive)); return 0; } diff --git a/include/linux/ide.h b/include/linux/ide.h index e99c56de7f56..62fccaea3110 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1533,6 +1533,7 @@ void ide_unregister_region(struct gendisk *); void ide_undecoded_slave(ide_drive_t *); void ide_port_apply_params(ide_hwif_t *); +int ide_sysfs_register_port(ide_hwif_t *); struct ide_host *ide_host_alloc(const struct ide_port_info *, hw_regs_t **); void ide_host_free(struct ide_host *); @@ -1627,6 +1628,9 @@ extern struct mutex ide_cfg_mtx; #define local_irq_set(flags) do { local_save_flags((flags)); local_irq_enable_in_hardirq(); } while (0) +char *ide_media_string(ide_drive_t *); + +extern struct device_attribute ide_dev_attrs[]; extern struct bus_type ide_bus_type; extern struct class *ide_port_class; -- cgit v1.2.3 From 295f00042aaf6b553b5f37348f89bab463d4a469 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 2 Jan 2009 16:12:48 +0100 Subject: ide: don't execute the next queued command from the hard-IRQ context (v2) * Tell the block layer that we are not done handling requests by using blk_plug_device() in ide_do_request() (request handling function) and ide_timer_expiry() (timeout handler) if the queue is not empty. * Remove optimization which directly calls ide_do_request() for the next queued command from the ide_intr() (IRQ handler) and ide_timer_expiry(). * Remove no longer needed IRQ masking from ide_do_request() - in case of IDE ports needing serialization disable_irq_nosync()/enable_irq() was used for the (possibly shared) IRQ of the other IDE port. * Put the misplaced comment in the right place in ide_do_request(). * Drop no longer needed 'int masked_irq' argument from ide_do_request(). * Merge ide_do_request() into do_ide_request(). * Remove no longer needed IDE_NO_IRQ define. While at it: * Don't use HWGROUP() macro in do_ide_request(). * Use __func__ in ide_intr(). This patch reduces IRQ hadling latency for IDE and improves the system-wide handling of shared IRQs (which should result in more timeout resistant and stable IDE systems). It also makes it possible to do some further changes later (i.e. replace some busy-waiting delays with sleeping equivalents). v2: Changes per review from Elias Oltmanns: - fix wrong goto statement in 'if (startstop == ide_stopped)' block - use spin_unlock_irq() - don't use obsolete HWIF() macro Cc: Elias Oltmanns Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 67 +++++++++++++++++++++++----------------------------- include/linux/ide.h | 7 ------ 2 files changed, 30 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index ecacc008fdaf..23754bc5e595 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -778,8 +778,10 @@ repeat: * the driver. This makes the driver much more friendlier to shared IRQs * than previous designs, while remaining 100% (?) SMP safe and capable. */ -static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) +void do_ide_request(struct request_queue *q) { + ide_drive_t *orig_drive = q->queuedata; + ide_hwgroup_t *hwgroup = orig_drive->hwif->hwgroup; ide_drive_t *drive; ide_hwif_t *hwif; struct request *rq; @@ -837,10 +839,14 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) } /* no more work for this hwgroup (for now) */ - return; + goto plug_device; } - again: - hwif = HWIF(drive); + + if (drive != orig_drive) + goto plug_device; +again: + hwif = drive->hwif; + if (hwif != hwgroup->hwif) { /* * set nIEN for previous hwif, drives in the @@ -888,41 +894,26 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) goto again; /* We clear busy, there should be no pending ATA command at this point. */ hwgroup->busy = 0; - break; + goto plug_device; } hwgroup->rq = rq; - /* - * Some systems have trouble with IDE IRQs arriving while - * the driver is still setting things up. So, here we disable - * the IRQ used by this interface while the request is being started. - * This may look bad at first, but pretty much the same thing - * happens anyway when any interrupt comes in, IDE or otherwise - * -- the kernel masks the IRQ while it is being handled. - */ - if (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq) - disable_irq_nosync(hwif->irq); - spin_unlock(&hwgroup->lock); - local_irq_enable_in_hardirq(); - /* allow other IRQs while we start this request */ + spin_unlock_irq(&hwgroup->lock); startstop = start_request(drive, rq); spin_lock_irq(&hwgroup->lock); - if (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq) - enable_irq(hwif->irq); - if (startstop == ide_stopped) + + if (startstop == ide_stopped) { hwgroup->busy = 0; + if (!elv_queue_empty(orig_drive->queue)) + blk_plug_device(orig_drive->queue); + } } -} + return; -/* - * Passes the stuff to ide_do_request - */ -void do_ide_request(struct request_queue *q) -{ - ide_drive_t *drive = q->queuedata; - - ide_do_request(HWGROUP(drive), IDE_NO_IRQ); +plug_device: + if (!elv_queue_empty(orig_drive->queue)) + blk_plug_device(orig_drive->queue); } /* @@ -1074,11 +1065,13 @@ void ide_timer_expiry (unsigned long data) drive->service_time = jiffies - drive->service_start; spin_lock_irq(&hwgroup->lock); enable_irq(hwif->irq); - if (startstop == ide_stopped) + if (startstop == ide_stopped) { hwgroup->busy = 0; + if (!elv_queue_empty(drive->queue)) + blk_plug_device(drive->queue); + } } } - ide_do_request(hwgroup, IDE_NO_IRQ); spin_unlock_irqrestore(&hwgroup->lock, flags); } @@ -1271,11 +1264,11 @@ irqreturn_t ide_intr (int irq, void *dev_id) if (startstop == ide_stopped) { if (hwgroup->handler == NULL) { /* paranoia */ hwgroup->busy = 0; - ide_do_request(hwgroup, hwif->irq); - } else { - printk(KERN_ERR "%s: ide_intr: huh? expected NULL handler " - "on exit\n", drive->name); - } + if (!elv_queue_empty(drive->queue)) + blk_plug_device(drive->queue); + } else + printk(KERN_ERR "%s: %s: huh? expected NULL handler " + "on exit\n", __func__, drive->name); } out_handled: irq_ret = IRQ_HANDLED; diff --git a/include/linux/ide.h b/include/linux/ide.h index 62fccaea3110..968ca8f60531 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -32,13 +32,6 @@ # define SUPPORT_VLB_SYNC 1 #endif -/* - * Used to indicate "no IRQ", should be a value that cannot be an IRQ - * number. - */ - -#define IDE_NO_IRQ (-1) - typedef unsigned char byte; /* used everywhere */ /* -- cgit v1.2.3 From 631de3708d595d153e8a510a3608689290f4c0ed Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 2 Jan 2009 16:12:50 +0100 Subject: ide: add ide_[un]lock_hwgroup() helpers Add ide_[un]lock_hwgroup() inline helpers for obtaining exclusive access to the given hwgroup and update the core code accordingly. [ This change besides making code saner results in more efficient use of ide_{get,release}_lock(). ] Cc: Michael Schmitz Cc: Geert Uytterhoeven Cc: Elias Oltmanns Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 32 +++++++++++--------------------- drivers/ide/ide-park.c | 2 +- include/linux/ide.h | 20 ++++++++++++++++++++ 3 files changed, 32 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index c60512196f61..ab480042757a 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -790,10 +790,7 @@ void do_ide_request(struct request_queue *q) /* caller must own hwgroup->lock */ BUG_ON(!irqs_disabled()); - while (!hwgroup->busy) { - hwgroup->busy = 1; - /* for atari only */ - ide_get_lock(ide_intr, hwgroup); + while (!ide_lock_hwgroup(hwgroup)) { drive = choose_drive(hwgroup); if (drive == NULL) { int sleeping = 0; @@ -825,17 +822,10 @@ void do_ide_request(struct request_queue *q) hwgroup->sleeping = 1; hwgroup->req_gen_timer = hwgroup->req_gen; mod_timer(&hwgroup->timer, sleep); - /* we purposely leave hwgroup->busy==1 + /* we purposely leave hwgroup locked * while sleeping */ - } else { - /* Ugly, but how can we sleep for the lock - * otherwise? perhaps from tq_disk? - */ - - /* for atari only */ - ide_release_lock(); - hwgroup->busy = 0; - } + } else + ide_unlock_hwgroup(hwgroup); /* no more work for this hwgroup (for now) */ goto plug_device; @@ -865,7 +855,7 @@ void do_ide_request(struct request_queue *q) */ rq = elv_next_request(drive->queue); if (!rq) { - hwgroup->busy = 0; + ide_unlock_hwgroup(hwgroup); break; } @@ -885,8 +875,8 @@ void do_ide_request(struct request_queue *q) if ((drive->dev_flags & IDE_DFLAG_BLOCKED) && blk_pm_request(rq) == 0 && (rq->cmd_flags & REQ_PREEMPT) == 0) { - /* We clear busy, there should be no pending ATA command at this point. */ - hwgroup->busy = 0; + /* there should be no pending command at this point */ + ide_unlock_hwgroup(hwgroup); goto plug_device; } @@ -897,7 +887,7 @@ void do_ide_request(struct request_queue *q) spin_lock_irq(&hwgroup->lock); if (startstop == ide_stopped) { - hwgroup->busy = 0; + ide_unlock_hwgroup(hwgroup); if (!elv_queue_empty(orig_drive->queue)) blk_plug_device(orig_drive->queue); } @@ -1001,7 +991,7 @@ void ide_timer_expiry (unsigned long data) */ if (hwgroup->sleeping) { hwgroup->sleeping = 0; - hwgroup->busy = 0; + ide_unlock_hwgroup(hwgroup); } } else { ide_drive_t *drive = hwgroup->drive; @@ -1056,7 +1046,7 @@ void ide_timer_expiry (unsigned long data) spin_lock_irq(&hwgroup->lock); enable_irq(hwif->irq); if (startstop == ide_stopped) { - hwgroup->busy = 0; + ide_unlock_hwgroup(hwgroup); if (!elv_queue_empty(drive->queue)) blk_plug_device(drive->queue); } @@ -1249,7 +1239,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) drive->service_time = jiffies - drive->service_start; if (startstop == ide_stopped) { if (hwgroup->handler == NULL) { /* paranoia */ - hwgroup->busy = 0; + ide_unlock_hwgroup(hwgroup); if (!elv_queue_empty(drive->queue)) blk_plug_device(drive->queue); } else diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c index 63d01c55f865..44c6787f8aeb 100644 --- a/drivers/ide/ide-park.c +++ b/drivers/ide/ide-park.c @@ -22,7 +22,7 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout) if (reset_timer && hwgroup->sleeping && del_timer(&hwgroup->timer)) { hwgroup->sleeping = 0; - hwgroup->busy = 0; + ide_unlock_hwgroup(hwgroup); blk_start_queueing(q); } spin_unlock_irq(&hwgroup->lock); diff --git a/include/linux/ide.h b/include/linux/ide.h index 968ca8f60531..f408d6123f14 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1280,6 +1280,26 @@ extern void ide_stall_queue(ide_drive_t *drive, unsigned long timeout); extern void ide_timer_expiry(unsigned long); extern irqreturn_t ide_intr(int irq, void *dev_id); + +static inline int ide_lock_hwgroup(ide_hwgroup_t *hwgroup) +{ + if (hwgroup->busy) + return 1; + + hwgroup->busy = 1; + /* for atari only */ + ide_get_lock(ide_intr, hwgroup); + + return 0; +} + +static inline void ide_unlock_hwgroup(ide_hwgroup_t *hwgroup) +{ + /* for atari only */ + ide_release_lock(); + hwgroup->busy = 0; +} + extern void do_ide_request(struct request_queue *); void ide_init_disk(struct gendisk *, ide_drive_t *); -- cgit v1.2.3 From 201bffa46466b4afdf7d29db8eca3fa5decb39c8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 2 Jan 2009 16:12:50 +0100 Subject: ide: use per-device request queue locks (v2) * Move hack for flush requests from choose_drive() to do_ide_request(). * Add ide_plug_device() helper and convert core IDE code from using per-hwgroup lock as a request lock to use the ->queue_lock instead. * Remove no longer needed: - choose_drive() function - WAKEUP() macro - 'sleeping' flag from ide_hwif_t - 'service_{start,time}' fields from ide_drive_t This patch results in much simpler and more maintainable code (besides being a scalability improvement). v2: * Fixes/improvements based on review from Elias: - take as many requests off the queue as possible - remove now redundant BUG_ON() Cc: Elias Oltmanns Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 214 +++++++++++++++--------------------------------- drivers/ide/ide-park.c | 13 +-- drivers/ide/ide-probe.c | 3 +- include/linux/ide.h | 4 - 4 files changed, 77 insertions(+), 157 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index ab480042757a..bb3248abf47d 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -667,85 +667,10 @@ void ide_stall_queue (ide_drive_t *drive, unsigned long timeout) drive->sleep = timeout + jiffies; drive->dev_flags |= IDE_DFLAG_SLEEPING; } - EXPORT_SYMBOL(ide_stall_queue); -#define WAKEUP(drive) ((drive)->service_start + 2 * (drive)->service_time) - -/** - * choose_drive - select a drive to service - * @hwgroup: hardware group to select on - * - * choose_drive() selects the next drive which will be serviced. - * This is necessary because the IDE layer can't issue commands - * to both drives on the same cable, unlike SCSI. - */ - -static inline ide_drive_t *choose_drive (ide_hwgroup_t *hwgroup) -{ - ide_drive_t *drive, *best; - -repeat: - best = NULL; - drive = hwgroup->drive; - - /* - * drive is doing pre-flush, ordered write, post-flush sequence. even - * though that is 3 requests, it must be seen as a single transaction. - * we must not preempt this drive until that is complete - */ - if (blk_queue_flushing(drive->queue)) { - /* - * small race where queue could get replugged during - * the 3-request flush cycle, just yank the plug since - * we want it to finish asap - */ - blk_remove_plug(drive->queue); - return drive; - } - - do { - u8 dev_s = !!(drive->dev_flags & IDE_DFLAG_SLEEPING); - u8 best_s = (best && !!(best->dev_flags & IDE_DFLAG_SLEEPING)); - - if ((dev_s == 0 || time_after_eq(jiffies, drive->sleep)) && - !elv_queue_empty(drive->queue)) { - if (best == NULL || - (dev_s && (best_s == 0 || time_before(drive->sleep, best->sleep))) || - (best_s == 0 && time_before(WAKEUP(drive), WAKEUP(best)))) { - if (!blk_queue_plugged(drive->queue)) - best = drive; - } - } - } while ((drive = drive->next) != hwgroup->drive); - - if (best && (best->dev_flags & IDE_DFLAG_NICE1) && - (best->dev_flags & IDE_DFLAG_SLEEPING) == 0 && - best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) { - long t = (signed long)(WAKEUP(best) - jiffies); - if (t >= WAIT_MIN_SLEEP) { - /* - * We *may* have some time to spare, but first let's see if - * someone can potentially benefit from our nice mood today.. - */ - drive = best->next; - do { - if ((drive->dev_flags & IDE_DFLAG_SLEEPING) == 0 - && time_before(jiffies - best->service_time, WAKEUP(drive)) - && time_before(WAKEUP(drive), jiffies + t)) - { - ide_stall_queue(best, min_t(long, t, 10 * WAIT_MIN_SLEEP)); - goto repeat; - } - } while ((drive = drive->next) != best); - } - } - return best; -} - /* * Issue a new request to a drive from hwgroup - * Caller must have already done spin_lock_irqsave(&hwgroup->lock, ..); * * A hwgroup is a serialized group of IDE interfaces. Usually there is * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640) @@ -757,8 +682,7 @@ repeat: * possibly along with many other devices. This is especially common in * PCI-based systems with off-board IDE controller cards. * - * The IDE driver uses a per-hwgroup spinlock to protect - * access to the request queues, and to protect the hwgroup->busy flag. + * The IDE driver uses a per-hwgroup lock to protect the hwgroup->busy flag. * * The first thread into the driver for a particular hwgroup sets the * hwgroup->busy flag to indicate that this hwgroup is now active, @@ -780,61 +704,38 @@ repeat: */ void do_ide_request(struct request_queue *q) { - ide_drive_t *orig_drive = q->queuedata; - ide_hwgroup_t *hwgroup = orig_drive->hwif->hwgroup; - ide_drive_t *drive; - ide_hwif_t *hwif; + ide_drive_t *drive = q->queuedata; + ide_hwif_t *hwif = drive->hwif; + ide_hwgroup_t *hwgroup = hwif->hwgroup; struct request *rq; ide_startstop_t startstop; - /* caller must own hwgroup->lock */ - BUG_ON(!irqs_disabled()); - - while (!ide_lock_hwgroup(hwgroup)) { - drive = choose_drive(hwgroup); - if (drive == NULL) { - int sleeping = 0; - unsigned long sleep = 0; /* shut up, gcc */ - hwgroup->rq = NULL; - drive = hwgroup->drive; - do { - if ((drive->dev_flags & IDE_DFLAG_SLEEPING) && - (sleeping == 0 || - time_before(drive->sleep, sleep))) { - sleeping = 1; - sleep = drive->sleep; - } - } while ((drive = drive->next) != hwgroup->drive); - if (sleeping) { + /* + * drive is doing pre-flush, ordered write, post-flush sequence. even + * though that is 3 requests, it must be seen as a single transaction. + * we must not preempt this drive until that is complete + */ + if (blk_queue_flushing(q)) /* - * Take a short snooze, and then wake up this hwgroup again. - * This gives other hwgroups on the same a chance to - * play fairly with us, just in case there are big differences - * in relative throughputs.. don't want to hog the cpu too much. + * small race where queue could get replugged during + * the 3-request flush cycle, just yank the plug since + * we want it to finish asap */ - if (time_before(sleep, jiffies + WAIT_MIN_SLEEP)) - sleep = jiffies + WAIT_MIN_SLEEP; -#if 1 - if (timer_pending(&hwgroup->timer)) - printk(KERN_CRIT "ide_set_handler: timer already active\n"); -#endif - /* so that ide_timer_expiry knows what to do */ - hwgroup->sleeping = 1; - hwgroup->req_gen_timer = hwgroup->req_gen; - mod_timer(&hwgroup->timer, sleep); - /* we purposely leave hwgroup locked - * while sleeping */ - } else - ide_unlock_hwgroup(hwgroup); + blk_remove_plug(q); - /* no more work for this hwgroup (for now) */ - goto plug_device; - } + spin_unlock_irq(q->queue_lock); + spin_lock_irq(&hwgroup->lock); - if (drive != orig_drive) - goto plug_device; + if (!ide_lock_hwgroup(hwgroup)) { +repeat: + hwgroup->rq = NULL; - hwif = drive->hwif; + if (drive->dev_flags & IDE_DFLAG_SLEEPING) { + if (time_before(drive->sleep, jiffies)) { + ide_unlock_hwgroup(hwgroup); + goto plug_device; + } + } if (hwif != hwgroup->hwif) { /* @@ -847,16 +748,20 @@ void do_ide_request(struct request_queue *q) hwgroup->hwif = hwif; hwgroup->drive = drive; drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED); - drive->service_start = jiffies; + spin_unlock_irq(&hwgroup->lock); + spin_lock_irq(q->queue_lock); /* * we know that the queue isn't empty, but this can happen * if the q->prep_rq_fn() decides to kill a request */ rq = elv_next_request(drive->queue); + spin_unlock_irq(q->queue_lock); + spin_lock_irq(&hwgroup->lock); + if (!rq) { ide_unlock_hwgroup(hwgroup); - break; + goto out; } /* @@ -886,17 +791,21 @@ void do_ide_request(struct request_queue *q) startstop = start_request(drive, rq); spin_lock_irq(&hwgroup->lock); - if (startstop == ide_stopped) { - ide_unlock_hwgroup(hwgroup); - if (!elv_queue_empty(orig_drive->queue)) - blk_plug_device(orig_drive->queue); - } - } + if (startstop == ide_stopped) + goto repeat; + } else + goto plug_device; +out: + spin_unlock_irq(&hwgroup->lock); + spin_lock_irq(q->queue_lock); return; plug_device: - if (!elv_queue_empty(orig_drive->queue)) - blk_plug_device(orig_drive->queue); + spin_unlock_irq(&hwgroup->lock); + spin_lock_irq(q->queue_lock); + + if (!elv_queue_empty(q)) + blk_plug_device(q); } /* @@ -957,6 +866,17 @@ out: return ret; } +static void ide_plug_device(ide_drive_t *drive) +{ + struct request_queue *q = drive->queue; + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + if (!elv_queue_empty(q)) + blk_plug_device(q); + spin_unlock_irqrestore(q->queue_lock, flags); +} + /** * ide_timer_expiry - handle lack of an IDE interrupt * @data: timer callback magic (hwgroup) @@ -974,10 +894,12 @@ out: void ide_timer_expiry (unsigned long data) { ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data; + ide_drive_t *uninitialized_var(drive); ide_handler_t *handler; ide_expiry_t *expiry; unsigned long flags; unsigned long wait = -1; + int plug_device = 0; spin_lock_irqsave(&hwgroup->lock, flags); @@ -989,12 +911,8 @@ void ide_timer_expiry (unsigned long data) * or we were "sleeping" to give other devices a chance. * Either way, we don't really want to complain about anything. */ - if (hwgroup->sleeping) { - hwgroup->sleeping = 0; - ide_unlock_hwgroup(hwgroup); - } } else { - ide_drive_t *drive = hwgroup->drive; + drive = hwgroup->drive; if (!drive) { printk(KERN_ERR "ide_timer_expiry: hwgroup->drive was NULL\n"); hwgroup->handler = NULL; @@ -1042,17 +960,18 @@ void ide_timer_expiry (unsigned long data) ide_error(drive, "irq timeout", hwif->tp_ops->read_status(hwif)); } - drive->service_time = jiffies - drive->service_start; spin_lock_irq(&hwgroup->lock); enable_irq(hwif->irq); if (startstop == ide_stopped) { ide_unlock_hwgroup(hwgroup); - if (!elv_queue_empty(drive->queue)) - blk_plug_device(drive->queue); + plug_device = 1; } } } spin_unlock_irqrestore(&hwgroup->lock, flags); + + if (plug_device) + ide_plug_device(drive); } /** @@ -1146,10 +1065,11 @@ irqreturn_t ide_intr (int irq, void *dev_id) unsigned long flags; ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id; ide_hwif_t *hwif = hwgroup->hwif; - ide_drive_t *drive; + ide_drive_t *uninitialized_var(drive); ide_handler_t *handler; ide_startstop_t startstop; irqreturn_t irq_ret = IRQ_NONE; + int plug_device = 0; spin_lock_irqsave(&hwgroup->lock, flags); @@ -1236,12 +1156,10 @@ irqreturn_t ide_intr (int irq, void *dev_id) * same irq as is currently being serviced here, and Linux * won't allow another of the same (on any CPU) until we return. */ - drive->service_time = jiffies - drive->service_start; if (startstop == ide_stopped) { if (hwgroup->handler == NULL) { /* paranoia */ ide_unlock_hwgroup(hwgroup); - if (!elv_queue_empty(drive->queue)) - blk_plug_device(drive->queue); + plug_device = 1; } else printk(KERN_ERR "%s: %s: huh? expected NULL handler " "on exit\n", __func__, drive->name); @@ -1250,6 +1168,10 @@ out_handled: irq_ret = IRQ_HANDLED; out: spin_unlock_irqrestore(&hwgroup->lock, flags); + + if (plug_device) + ide_plug_device(drive); + return irq_ret; } diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c index 44c6787f8aeb..678454ac2483 100644 --- a/drivers/ide/ide-park.c +++ b/drivers/ide/ide-park.c @@ -16,16 +16,19 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout) spin_lock_irq(&hwgroup->lock); if (drive->dev_flags & IDE_DFLAG_PARKED) { int reset_timer = time_before(timeout, drive->sleep); + int start_queue = 0; drive->sleep = timeout; wake_up_all(&ide_park_wq); - if (reset_timer && hwgroup->sleeping && - del_timer(&hwgroup->timer)) { - hwgroup->sleeping = 0; - ide_unlock_hwgroup(hwgroup); + if (reset_timer && del_timer(&hwgroup->timer)) + start_queue = 1; + spin_unlock_irq(&hwgroup->lock); + + if (start_queue) { + spin_lock_irq(q->queue_lock); blk_start_queueing(q); + spin_unlock_irq(q->queue_lock); } - spin_unlock_irq(&hwgroup->lock); return; } spin_unlock_irq(&hwgroup->lock); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index f9efd069edc2..966b74c15773 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -881,8 +881,7 @@ static int ide_init_queue(ide_drive_t *drive) * do not. */ - q = blk_init_queue_node(do_ide_request, &hwif->hwgroup->lock, - hwif_to_node(hwif)); + q = blk_init_queue_node(do_ide_request, NULL, hwif_to_node(hwif)); if (!q) return 1; diff --git a/include/linux/ide.h b/include/linux/ide.h index f408d6123f14..5f86ad40ee7e 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -603,8 +603,6 @@ struct ide_drive_s { unsigned long dev_flags; unsigned long sleep; /* sleep until this time */ - unsigned long service_start; /* time we started last request */ - unsigned long service_time; /* service time of last request */ unsigned long timeout; /* max time to wait for irq */ special_t special; /* special action flags */ @@ -872,8 +870,6 @@ typedef struct hwgroup_s { /* BOOL: protects all fields below */ volatile int busy; - /* BOOL: wake us up on timer expiry */ - unsigned int sleeping : 1; /* BOOL: polling active & poll_timeout field valid */ unsigned int polling : 1; -- cgit v1.2.3 From bf64741fe89280bd81a9e3a1beadec1570861848 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 2 Jan 2009 16:12:50 +0100 Subject: ide: make IDE_AFLAG_.. numbering continuous again Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index 5f86ad40ee7e..eb4c01f7f253 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -473,53 +473,53 @@ enum { /* ide-cd */ /* Drive cannot eject the disc. */ - IDE_AFLAG_NO_EJECT = (1 << 3), + IDE_AFLAG_NO_EJECT = (1 << 1), /* Drive is a pre ATAPI 1.2 drive. */ - IDE_AFLAG_PRE_ATAPI12 = (1 << 4), + IDE_AFLAG_PRE_ATAPI12 = (1 << 2), /* TOC addresses are in BCD. */ - IDE_AFLAG_TOCADDR_AS_BCD = (1 << 5), + IDE_AFLAG_TOCADDR_AS_BCD = (1 << 3), /* TOC track numbers are in BCD. */ - IDE_AFLAG_TOCTRACKS_AS_BCD = (1 << 6), + IDE_AFLAG_TOCTRACKS_AS_BCD = (1 << 4), /* * Drive does not provide data in multiples of SECTOR_SIZE * when more than one interrupt is needed. */ - IDE_AFLAG_LIMIT_NFRAMES = (1 << 7), + IDE_AFLAG_LIMIT_NFRAMES = (1 << 5), /* Saved TOC information is current. */ - IDE_AFLAG_TOC_VALID = (1 << 9), + IDE_AFLAG_TOC_VALID = (1 << 6), /* We think that the drive door is locked. */ - IDE_AFLAG_DOOR_LOCKED = (1 << 10), + IDE_AFLAG_DOOR_LOCKED = (1 << 7), /* SET_CD_SPEED command is unsupported. */ - IDE_AFLAG_NO_SPEED_SELECT = (1 << 11), - IDE_AFLAG_VERTOS_300_SSD = (1 << 12), - IDE_AFLAG_VERTOS_600_ESD = (1 << 13), - IDE_AFLAG_SANYO_3CD = (1 << 14), - IDE_AFLAG_FULL_CAPS_PAGE = (1 << 15), - IDE_AFLAG_PLAY_AUDIO_OK = (1 << 16), - IDE_AFLAG_LE_SPEED_FIELDS = (1 << 17), + IDE_AFLAG_NO_SPEED_SELECT = (1 << 8), + IDE_AFLAG_VERTOS_300_SSD = (1 << 9), + IDE_AFLAG_VERTOS_600_ESD = (1 << 10), + IDE_AFLAG_SANYO_3CD = (1 << 11), + IDE_AFLAG_FULL_CAPS_PAGE = (1 << 12), + IDE_AFLAG_PLAY_AUDIO_OK = (1 << 13), + IDE_AFLAG_LE_SPEED_FIELDS = (1 << 14), /* ide-floppy */ /* Avoid commands not supported in Clik drive */ - IDE_AFLAG_CLIK_DRIVE = (1 << 19), + IDE_AFLAG_CLIK_DRIVE = (1 << 15), /* Requires BH algorithm for packets */ - IDE_AFLAG_ZIP_DRIVE = (1 << 20), + IDE_AFLAG_ZIP_DRIVE = (1 << 16), /* Supports format progress report */ - IDE_AFLAG_SRFP = (1 << 22), + IDE_AFLAG_SRFP = (1 << 17), /* ide-tape */ - IDE_AFLAG_IGNORE_DSC = (1 << 23), + IDE_AFLAG_IGNORE_DSC = (1 << 18), /* 0 When the tape position is unknown */ - IDE_AFLAG_ADDRESS_VALID = (1 << 24), + IDE_AFLAG_ADDRESS_VALID = (1 << 19), /* Device already opened */ - IDE_AFLAG_BUSY = (1 << 25), + IDE_AFLAG_BUSY = (1 << 20), /* Attempt to auto-detect the current user block size */ - IDE_AFLAG_DETECT_BS = (1 << 26), + IDE_AFLAG_DETECT_BS = (1 << 21), /* Currently on a filemark */ - IDE_AFLAG_FILEMARK = (1 << 27), + IDE_AFLAG_FILEMARK = (1 << 22), /* 0 = no tape is loaded, so we don't rewind after ejecting */ - IDE_AFLAG_MEDIUM_PRESENT = (1 << 28), + IDE_AFLAG_MEDIUM_PRESENT = (1 << 23), - IDE_AFLAG_NO_AUTOCLOSE = (1 << 29), + IDE_AFLAG_NO_AUTOCLOSE = (1 << 24), }; /* device flags */ -- cgit v1.2.3 From 392de1d53dd40e2eebee3a0a26aa647a3865ca78 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 2 Jan 2009 16:12:52 +0100 Subject: ide-atapi: accomodate transfer length calculation for ide-cd ... by factoring it out of ide_cd_do_request() into a helper, as suggested by Bart. There should be no functionality change resulting from this patch. Signed-off-by: Borislav Petkov [bart: BLK_DEV_IDECD needs to select IDE_ATAPI now] Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Kconfig | 1 + drivers/ide/ide-atapi.c | 15 ++++++++++++++- drivers/ide/ide-cd.c | 4 ++-- include/linux/ide.h | 2 ++ 4 files changed, 19 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 937945e471df..4ee85fcf9aaf 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -137,6 +137,7 @@ config BLK_DEV_DELKIN config BLK_DEV_IDECD tristate "Include IDE/ATAPI CDROM support" + select IDE_ATAPI ---help--- If you have a CD-ROM drive using the ATAPI protocol, say Y. ATAPI is a newer protocol used by IDE CD-ROM and TAPE drives, similar to the diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 74273fdc8827..8884877bd2b5 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -252,6 +252,18 @@ int ide_scsi_expiry(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_scsi_expiry); +int ide_cd_get_xferlen(struct request *rq) +{ + if (blk_fs_request(rq)) + return 32768; + else if (blk_sense_request(rq) || blk_pc_request(rq) || + rq->cmd_type == REQ_TYPE_ATA_PC) + return rq->data_len; + else + return 0; +} +EXPORT_SYMBOL_GPL(ide_cd_get_xferlen); + /* * This is the usual interrupt handler which will be called during a packet * command. We will transfer some of the data (as requested by the drive) @@ -551,7 +563,7 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; u32 tf_flags; - u16 bcount = 0; + u16 bcount; u8 scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI); /* We haven't transferred any data yet */ @@ -560,6 +572,7 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, if (dev_is_idecd(drive)) { tf_flags = IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL; + bcount = ide_cd_get_xferlen(hwif->hwgroup->rq); } else if (scsi) { tf_flags = 0; bcount = min(pc->req_xfer, 63 * 1024); diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 65e5513758b0..8d3c7714682e 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1214,8 +1214,9 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, __func__, rq->cmd[0], rq->cmd_type, (unsigned long long)block); + xferlen = ide_cd_get_xferlen(rq); + if (blk_fs_request(rq)) { - xferlen = 32768; fn = cdrom_start_rw_cont; if (cdrom_start_rw(drive, rq) == ide_stopped) @@ -1225,7 +1226,6 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, return ide_stopped; } else if (blk_sense_request(rq) || blk_pc_request(rq) || rq->cmd_type == REQ_TYPE_ATA_PC) { - xferlen = rq->data_len; fn = cdrom_do_newpc_cont; if (!rq->timeout) diff --git a/include/linux/ide.h b/include/linux/ide.h index eb4c01f7f253..e35ff6827897 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1254,6 +1254,8 @@ static inline unsigned long ide_scsi_get_timeout(struct ide_atapi_pc *pc) int ide_scsi_expiry(ide_drive_t *); +int ide_cd_get_xferlen(struct request *); + ide_startstop_t ide_issue_pc(ide_drive_t *, unsigned int, ide_expiry_t *); ide_startstop_t do_rw_taskfile(ide_drive_t *, ide_task_t *); -- cgit v1.2.3 From 4cad085efbce8dcc5006b0d1034089758b4fc7ba Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 2 Jan 2009 16:12:53 +0100 Subject: ide-cd: move cdrom_timer_expiry to ide-atapi.c - cdrom_timer_expiry -> ide_cd_expiry - remove expiry-arg to ide_issue_pc as it is redundant now - ide_debug_log -> debug_log Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 41 ++++++++++++++++++++++++++++++++++++++--- drivers/ide/ide-cd.c | 38 +++----------------------------------- drivers/ide/ide-cd.h | 4 ---- drivers/ide/ide-floppy.c | 2 +- drivers/ide/ide-tape.c | 2 +- include/linux/ide.h | 4 +++- 6 files changed, 46 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 8c5cf68fbd79..c110329ccb13 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -3,6 +3,7 @@ */ #include +#include #include #include #include @@ -252,6 +253,38 @@ int ide_scsi_expiry(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_scsi_expiry); +int ide_cd_expiry(ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + unsigned long wait = 0; + + debug_log("%s: rq->cmd[0]: 0x%x\n", __func__, rq->cmd[0]); + + /* + * Some commands are *slow* and normally take a long time to complete. + * Usually we can use the ATAPI "disconnect" to bypass this, but not all + * commands/drives support that. Let ide_timer_expiry keep polling us + * for these. + */ + switch (rq->cmd[0]) { + case GPCMD_BLANK: + case GPCMD_FORMAT_UNIT: + case GPCMD_RESERVE_RZONE_TRACK: + case GPCMD_CLOSE_TRACK: + case GPCMD_FLUSH_CACHE: + wait = ATAPI_WAIT_PC; + break; + default: + if (!(rq->cmd_flags & REQ_QUIET)) + printk(KERN_INFO "cmd 0x%x timed out\n", + rq->cmd[0]); + wait = 0; + break; + } + return wait; +} +EXPORT_SYMBOL_GPL(ide_cd_expiry); + int ide_cd_get_xferlen(struct request *rq) { if (blk_fs_request(rq)) @@ -562,11 +595,11 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) return ide_started; } -ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, - ide_expiry_t *expiry) +ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout) { struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; + ide_expiry_t *expiry = NULL; u32 tf_flags; u16 bcount; u8 scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI); @@ -578,9 +611,11 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, if (dev_is_idecd(drive)) { tf_flags = IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL; bcount = ide_cd_get_xferlen(hwif->hwgroup->rq); + expiry = ide_cd_expiry; } else if (scsi) { tf_flags = 0; bcount = min(pc->req_xfer, 63 * 1024); + expiry = ide_scsi_expiry; } else { tf_flags = IDE_TFLAG_OUT_DEVICE; bcount = ((drive->media == ide_tape) ? @@ -613,7 +648,7 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, if (drive->dma) drive->waiting_for_dma = 0; ide_execute_command(drive, ATA_CMD_PACKET, ide_transfer_pc, - timeout, NULL); + timeout, expiry); return ide_started; } else { ide_execute_pkt_cmd(drive); diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 8d3c7714682e..105e4d855e6e 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -511,38 +511,6 @@ end_request: return 1; } -static int cdrom_timer_expiry(ide_drive_t *drive) -{ - struct request *rq = HWGROUP(drive)->rq; - unsigned long wait = 0; - - ide_debug_log(IDE_DBG_RQ, "Call %s: rq->cmd[0]: 0x%x\n", __func__, - rq->cmd[0]); - - /* - * Some commands are *slow* and normally take a long time to complete. - * Usually we can use the ATAPI "disconnect" to bypass this, but not all - * commands/drives support that. Let ide_timer_expiry keep polling us - * for these. - */ - switch (rq->cmd[0]) { - case GPCMD_BLANK: - case GPCMD_FORMAT_UNIT: - case GPCMD_RESERVE_RZONE_TRACK: - case GPCMD_CLOSE_TRACK: - case GPCMD_FLUSH_CACHE: - wait = ATAPI_WAIT_PC; - break; - default: - if (!(rq->cmd_flags & REQ_QUIET)) - printk(KERN_INFO PFX "cmd 0x%x timed out\n", - rq->cmd[0]); - wait = 0; - break; - } - return wait; -} - /* * Set up the device registers for transferring a packet command on DEV, * expecting to later transfer XFERLEN bytes. HANDLER is the routine @@ -574,7 +542,7 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, /* packet command */ ide_execute_command(drive, ATA_CMD_PACKET, handler, - ATAPI_WAIT_PC, cdrom_timer_expiry); + ATAPI_WAIT_PC, ide_cd_expiry); return ide_started; } else { ide_execute_pkt_cmd(drive); @@ -621,7 +589,7 @@ static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, } /* arm the interrupt handler */ - ide_set_handler(drive, handler, rq->timeout, cdrom_timer_expiry); + ide_set_handler(drive, handler, rq->timeout, ide_cd_expiry); /* ATAPI commands get padded out to 12 bytes minimum */ cmd_len = COMMAND_SIZE(rq->cmd[0]); @@ -1088,7 +1056,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) } else { timeout = ATAPI_WAIT_PC; if (!blk_fs_request(rq)) - expiry = cdrom_timer_expiry; + expiry = ide_cd_expiry; } ide_set_handler(drive, cdrom_newpc_intr, timeout, expiry); diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h index 389faa42eaa1..bf676b262181 100644 --- a/drivers/ide/ide-cd.h +++ b/drivers/ide/ide-cd.h @@ -16,10 +16,6 @@ #define ide_debug_log(lvl, fmt, args...) do {} while (0) #endif -/* - * typical timeout for packet command - */ -#define ATAPI_WAIT_PC (60 * HZ) #define ATAPI_WAIT_WRITE_BUSY (10 * HZ) /************************************************************************/ diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 1f07f3818938..fdec729d0e49 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -197,7 +197,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, pc->retries++; - return ide_issue_pc(drive, WAIT_FLOPPY_CMD, NULL); + return ide_issue_pc(drive, WAIT_FLOPPY_CMD); } void ide_floppy_create_read_capacity_cmd(struct ide_atapi_pc *pc) diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index a2d470eb2b55..ac9e29a4991f 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -694,7 +694,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, pc->retries++; - return ide_issue_pc(drive, WAIT_TAPE_CMD, NULL); + return ide_issue_pc(drive, WAIT_TAPE_CMD); } /* A mode sense command is used to "sense" tape parameters. */ diff --git a/include/linux/ide.h b/include/linux/ide.h index e35ff6827897..e20e0b5c1739 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -396,6 +396,7 @@ enum { * This is used for several packet commands (not for READ/WRITE commands). */ #define IDE_PC_BUFFER_SIZE 256 +#define ATAPI_WAIT_PC (60 * HZ) struct ide_atapi_pc { /* actual packet bytes */ @@ -1253,10 +1254,11 @@ static inline unsigned long ide_scsi_get_timeout(struct ide_atapi_pc *pc) } int ide_scsi_expiry(ide_drive_t *); +int ide_cd_expiry(ide_drive_t *); int ide_cd_get_xferlen(struct request *); -ide_startstop_t ide_issue_pc(ide_drive_t *, unsigned int, ide_expiry_t *); +ide_startstop_t ide_issue_pc(ide_drive_t *, unsigned int); ide_startstop_t do_rw_taskfile(ide_drive_t *, ide_task_t *); -- cgit v1.2.3 From 5d655a03b847fbe5353a8a74bbeb75e18708dca3 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 2 Jan 2009 16:12:54 +0100 Subject: ide-atapi: remove ide-scsi remnants from ide_pc_intr() As a result, remove now unused ide_scsi_get_timeout and ide_scsi_expiry. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 68 ++++++++++--------------------------------------- include/linux/ide.h | 6 ----- 2 files changed, 13 insertions(+), 61 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index f5bf405c36aa..7a04509bf962 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -240,19 +240,6 @@ void ide_retry_pc(ide_drive_t *drive, struct gendisk *disk) } EXPORT_SYMBOL_GPL(ide_retry_pc); -int ide_scsi_expiry(ide_drive_t *drive) -{ - struct ide_atapi_pc *pc = drive->pc; - - debug_log("%s called for %lu at %lu\n", __func__, - pc->scsi_cmd->serial_number, jiffies); - - pc->flags |= PC_FLAG_TIMEDOUT; - - return 0; /* we do not want the IDE subsystem to retry */ -} -EXPORT_SYMBOL_GPL(ide_scsi_expiry); - int ide_cd_expiry(ide_drive_t *drive) { struct request *rq = HWGROUP(drive)->rq; @@ -309,21 +296,14 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) struct request *rq = hwif->hwgroup->rq; const struct ide_tp_ops *tp_ops = hwif->tp_ops; xfer_func_t *xferfunc; - ide_expiry_t *expiry; unsigned int timeout, temp; u16 bcount; - u8 stat, ireason, scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI), dsc = 0; + u8 stat, ireason, dsc = 0; debug_log("Enter %s - interrupt handler\n", __func__); - if (scsi) { - timeout = ide_scsi_get_timeout(pc); - expiry = ide_scsi_expiry; - } else { - timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD - : WAIT_TAPE_CMD; - expiry = NULL; - } + timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD + : WAIT_TAPE_CMD; if (pc->flags & PC_FLAG_TIMEDOUT) { drive->pc_callback(drive, 0); @@ -335,8 +315,8 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { if (hwif->dma_ops->dma_end(drive) || - (drive->media == ide_tape && !scsi && (stat & ATA_ERR))) { - if (drive->media == ide_floppy && !scsi) + (drive->media == ide_tape && (stat & ATA_ERR))) { + if (drive->media == ide_floppy) printk(KERN_ERR "%s: DMA %s error\n", drive->name, rq_data_dir(pc->rq) ? "write" : "read"); @@ -358,7 +338,7 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) local_irq_enable_in_hardirq(); - if (drive->media == ide_tape && !scsi && + if (drive->media == ide_tape && (stat & ATA_ERR) && rq->cmd[0] == REQUEST_SENSE) stat &= ~ATA_ERR; @@ -366,11 +346,8 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) /* Error detected */ debug_log("%s: I/O error\n", drive->name); - if (drive->media != ide_tape || scsi) { + if (drive->media != ide_tape) pc->rq->errors++; - if (scsi) - goto cmd_finished; - } if (rq->cmd[0] == REQUEST_SENSE) { printk(KERN_ERR "%s: I/O error in request sense" @@ -386,7 +363,6 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) /* queued, but not started */ return ide_stopped; } -cmd_finished: pc->error = 0; if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && (stat & ATA_DSC) == 0) @@ -433,25 +409,8 @@ cmd_finished: "us more data than expected - " "discarding data\n", drive->name); - if (scsi) - temp = pc->buf_size - pc->xferred; - else - temp = 0; - if (temp) { - if (pc->sg) - drive->pc_io_buffers(drive, pc, - temp, 0); - else - tp_ops->input_data(drive, NULL, - pc->cur_pos, temp); - printk(KERN_ERR "%s: transferred %d of " - "%d bytes\n", - drive->name, - temp, bcount); - } - pc->xferred += temp; - pc->cur_pos += temp; - ide_pad_transfer(drive, 0, bcount - temp); + + ide_pad_transfer(drive, 0, bcount); goto next_irq; } debug_log("The device wants to send us more data than " @@ -461,14 +420,13 @@ cmd_finished: } else xferfunc = tp_ops->output_data; - if ((drive->media == ide_floppy && !scsi && !pc->buf) || - (drive->media == ide_tape && !scsi && pc->bh) || - (scsi && pc->sg)) { + if ((drive->media == ide_floppy && !pc->buf) || + (drive->media == ide_tape && pc->bh)) { int done = drive->pc_io_buffers(drive, pc, bcount, !!(pc->flags & PC_FLAG_WRITING)); /* FIXME: don't do partial completions */ - if (drive->media == ide_floppy && !scsi) + if (drive->media == ide_floppy) ide_end_request(drive, 1, done >> 9); } else xferfunc(drive, NULL, pc->cur_pos, bcount); @@ -481,7 +439,7 @@ cmd_finished: rq->cmd[0], bcount); next_irq: /* And set the interrupt handler again */ - ide_set_handler(drive, ide_pc_intr, timeout, expiry); + ide_set_handler(drive, ide_pc_intr, timeout, NULL); return ide_started; } diff --git a/include/linux/ide.h b/include/linux/ide.h index e20e0b5c1739..257524ee1af2 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1248,12 +1248,6 @@ int ide_set_media_lock(ide_drive_t *, struct gendisk *, int); void ide_create_request_sense_cmd(ide_drive_t *, struct ide_atapi_pc *); void ide_retry_pc(ide_drive_t *, struct gendisk *); -static inline unsigned long ide_scsi_get_timeout(struct ide_atapi_pc *pc) -{ - return max_t(unsigned long, WAIT_CMD, pc->timeout - jiffies); -} - -int ide_scsi_expiry(ide_drive_t *); int ide_cd_expiry(ide_drive_t *); int ide_cd_get_xferlen(struct request *); -- cgit v1.2.3 From 5317464dccd0c03026d60f1e9968de4f9cd23f69 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 2 Jan 2009 16:12:54 +0100 Subject: ide: remove the last ide-scsi remnants Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 3 +-- drivers/ide/ide-io.c | 3 --- drivers/ide/ide-ioctls.c | 3 +-- drivers/ide/ide-probe.c | 2 -- include/linux/ide.h | 28 +++++++++++++--------------- 5 files changed, 15 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 7a04509bf962..d412bd2bd7fd 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -17,8 +17,7 @@ static inline int dev_is_idecd(ide_drive_t *drive) { - return (drive->media == ide_cdrom || drive->media == ide_optical) && - !(drive->dev_flags & IDE_DFLAG_SCSI); + return drive->media == ide_cdrom || drive->media == ide_optical; } /* diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index bb3248abf47d..1c36a8e83d36 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -426,9 +426,6 @@ void ide_map_sg(ide_drive_t *drive, struct request *rq) ide_hwif_t *hwif = drive->hwif; struct scatterlist *sg = hwif->sg_table; - if (hwif->sg_mapped) /* needed by ide-scsi */ - return; - if (rq->cmd_type != REQ_TYPE_ATA_TASKFILE) { hwif->sg_nents = blk_rq_map_sg(drive->queue, rq, sg); } else { diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c index 28232c64c346..1be263eb9c07 100644 --- a/drivers/ide/ide-ioctls.c +++ b/drivers/ide/ide-ioctls.c @@ -95,8 +95,7 @@ static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg) return -EPERM; if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) && - (drive->media != ide_tape || - (drive->dev_flags & IDE_DFLAG_SCSI))) + (drive->media != ide_tape)) return -EPERM; if ((arg >> IDE_NICE_DSC_OVERLAP) & 1) diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 966b74c15773..c5adb7b9c5b5 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1141,8 +1141,6 @@ static struct kobject *ata_probe(dev_t dev, int *part, void *data) if (drive->media == ide_disk) request_module("ide-disk"); - if (drive->dev_flags & IDE_DFLAG_SCSI) - request_module("ide-scsi"); if (drive->media == ide_cdrom || drive->media == ide_optical) request_module("ide-cd"); if (drive->media == ide_tape) diff --git a/include/linux/ide.h b/include/linux/ide.h index 257524ee1af2..ad57a4492941 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -559,28 +559,26 @@ enum { IDE_DFLAG_NODMA = (1 << 16), /* powermanagment told us not to do anything, so sleep nicely */ IDE_DFLAG_BLOCKED = (1 << 17), - /* ide-scsi emulation */ - IDE_DFLAG_SCSI = (1 << 18), /* sleeping & sleep field valid */ - IDE_DFLAG_SLEEPING = (1 << 19), - IDE_DFLAG_POST_RESET = (1 << 20), - IDE_DFLAG_UDMA33_WARNED = (1 << 21), - IDE_DFLAG_LBA48 = (1 << 22), + IDE_DFLAG_SLEEPING = (1 << 18), + IDE_DFLAG_POST_RESET = (1 << 19), + IDE_DFLAG_UDMA33_WARNED = (1 << 20), + IDE_DFLAG_LBA48 = (1 << 21), /* status of write cache */ - IDE_DFLAG_WCACHE = (1 << 23), + IDE_DFLAG_WCACHE = (1 << 22), /* used for ignoring ATA_DF */ - IDE_DFLAG_NOWERR = (1 << 24), + IDE_DFLAG_NOWERR = (1 << 23), /* retrying in PIO */ - IDE_DFLAG_DMA_PIO_RETRY = (1 << 25), - IDE_DFLAG_LBA = (1 << 26), + IDE_DFLAG_DMA_PIO_RETRY = (1 << 24), + IDE_DFLAG_LBA = (1 << 25), /* don't unload heads */ - IDE_DFLAG_NO_UNLOAD = (1 << 27), + IDE_DFLAG_NO_UNLOAD = (1 << 26), /* heads unloaded, please don't reset port */ - IDE_DFLAG_PARKED = (1 << 28), - IDE_DFLAG_MEDIA_CHANGED = (1 << 29), + IDE_DFLAG_PARKED = (1 << 27), + IDE_DFLAG_MEDIA_CHANGED = (1 << 28), /* write protect */ - IDE_DFLAG_WP = (1 << 30), - IDE_DFLAG_FORMAT_IN_PROGRESS = (1 << 31), + IDE_DFLAG_WP = (1 << 29), + IDE_DFLAG_FORMAT_IN_PROGRESS = (1 << 30), }; struct ide_drive_s { -- cgit v1.2.3 From 28ad91db77755f1c49d79652de11b28ee2cfbf03 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 2 Jan 2009 16:12:56 +0100 Subject: ide-atapi: remove timeout arg to ide_issue_pc There should be no functionality change resulting from this patch. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 7 ++++++- drivers/ide/ide-floppy.c | 2 +- drivers/ide/ide-tape.c | 2 +- include/linux/ide.h | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index fa7a70a3f24c..c470dbb155ca 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -562,11 +562,12 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) return ide_started; } -ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout) +ide_startstop_t ide_issue_pc(ide_drive_t *drive) { struct ide_atapi_pc *pc; ide_hwif_t *hwif = drive->hwif; ide_expiry_t *expiry = NULL; + unsigned int timeout; u32 tf_flags; u16 bcount; @@ -574,6 +575,7 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout) tf_flags = IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL; bcount = ide_cd_get_xferlen(hwif->hwgroup->rq); expiry = ide_cd_expiry; + timeout = ATAPI_WAIT_PC; if (drive->dma) drive->dma = !hwif->dma_ops->dma_setup(drive); @@ -600,6 +602,9 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout) if (!drive->dma) pc->flags &= ~PC_FLAG_DMA_OK; + + timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD + : WAIT_TAPE_CMD; } ide_pktcmd_tf_load(drive, tf_flags, bcount, drive->dma); diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index fdec729d0e49..0a48e2dc53a2 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -197,7 +197,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, pc->retries++; - return ide_issue_pc(drive, WAIT_FLOPPY_CMD); + return ide_issue_pc(drive); } void ide_floppy_create_read_capacity_cmd(struct ide_atapi_pc *pc) diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index ac9e29a4991f..5d2aa22cd6e4 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -694,7 +694,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, pc->retries++; - return ide_issue_pc(drive, WAIT_TAPE_CMD); + return ide_issue_pc(drive); } /* A mode sense command is used to "sense" tape parameters. */ diff --git a/include/linux/ide.h b/include/linux/ide.h index ad57a4492941..db5ef8ae1ab9 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1250,7 +1250,7 @@ int ide_cd_expiry(ide_drive_t *); int ide_cd_get_xferlen(struct request *); -ide_startstop_t ide_issue_pc(ide_drive_t *, unsigned int); +ide_startstop_t ide_issue_pc(ide_drive_t *); ide_startstop_t do_rw_taskfile(ide_drive_t *, ide_task_t *); -- cgit v1.2.3 From f153b82121b0366fe0e5f9553545cce237335175 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 2 Jan 2009 09:23:03 -0800 Subject: Sanitize gcc version header includes - include the gcc version-dependent header files from the generic gcc header file, rather than the other way around (iow: don't make the non-gcc header file have to know about gcc versions) - don't include compiler-gcc4.h for gcc 5 (for whenever it gets released). That's just confusing and made us do odd things in the gcc4 header file (testing that we really had version 4!) - generate the name from the __GNUC__ version directly, rather than having a mess of #if conditionals. Signed-off-by: Linus Torvalds --- include/linux/compiler-gcc.h | 5 +++++ include/linux/compiler-gcc3.h | 3 --- include/linux/compiler-gcc4.h | 5 +---- include/linux/compiler.h | 8 ++------ 4 files changed, 8 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h index 5c8351b859f0..af40f8eb86f0 100644 --- a/include/linux/compiler-gcc.h +++ b/include/linux/compiler-gcc.h @@ -61,3 +61,8 @@ #define noinline __attribute__((noinline)) #define __attribute_const__ __attribute__((__const__)) #define __maybe_unused __attribute__((unused)) + +#define __gcc_header(x) #x +#define _gcc_header(x) __gcc_header(linux/compiler-gcc##x.h) +#define gcc_header(x) _gcc_header(x) +#include gcc_header(__GNUC__) diff --git a/include/linux/compiler-gcc3.h b/include/linux/compiler-gcc3.h index e5eb795f78a1..2befe6513ce4 100644 --- a/include/linux/compiler-gcc3.h +++ b/include/linux/compiler-gcc3.h @@ -2,9 +2,6 @@ #error "Please don't include directly, include instead." #endif -/* These definitions are for GCC v3.x. */ -#include - #if __GNUC_MINOR__ >= 3 # define __used __attribute__((__used__)) #else diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h index 974f5b7bb205..aa426214331b 100644 --- a/include/linux/compiler-gcc4.h +++ b/include/linux/compiler-gcc4.h @@ -2,9 +2,6 @@ #error "Please don't include directly, include instead." #endif -/* These definitions are for GCC v4.x. */ -#include - #define __used __attribute__((__used__)) #define __must_check __attribute__((warn_unused_result)) #define __compiler_offsetof(a,b) __builtin_offsetof(a,b) @@ -16,7 +13,7 @@ */ #define uninitialized_var(x) x = x -#if !(__GNUC__ == 4 && __GNUC_MINOR__ < 3) +#if __GNUC_MINOR__ >= 3 /* Mark functions as cold. gcc will assume any path leading to a call to them will be unlikely. This means a lot of manual unlikely()s are unnecessary now for any paths leading to the usual suspects diff --git a/include/linux/compiler.h b/include/linux/compiler.h index ea7c6be354b7..d95da1020f1c 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -36,12 +36,8 @@ extern void __chk_io_ptr(const volatile void __iomem *); #ifdef __KERNEL__ -#if __GNUC__ >= 4 -# include -#elif __GNUC__ == 3 && __GNUC_MINOR__ >= 2 -# include -#else -# error Sorry, your compiler is too old/not recognized. +#ifdef __GNUC__ +#include #endif #define notrace __attribute__((no_instrument_function)) -- cgit v1.2.3 From f9d14250071eda9972e4c9cea745a11185952114 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 2 Jan 2009 09:29:43 -0800 Subject: Disallow gcc versions 4.1.{0,1} These compiler versions are known to miscompile __weak functions and thus generate kernels that don't necessarily work correctly. If a weak function is int he same compilation unit as a caller, gcc may end up inlining it, and thus binding the weak function too early. See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=27781 for details. Cc: Adrian Bunk Cc: Helge Deller Cc: Rusty Russell Cc: Ingo Molnar Signed-off-by: Linus Torvalds --- include/linux/compiler-gcc4.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h index aa426214331b..09992718f9e8 100644 --- a/include/linux/compiler-gcc4.h +++ b/include/linux/compiler-gcc4.h @@ -2,6 +2,11 @@ #error "Please don't include directly, include instead." #endif +/* GCC 4.1.[01] miscompiles __weak */ +#if __GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ <= 1 +# error Your version of gcc miscompiles the __weak directive +#endif + #define __used __attribute__((__used__)) #define __must_check __attribute__((warn_unused_result)) #define __compiler_offsetof(a,b) __builtin_offsetof(a,b) -- cgit v1.2.3 From a88a69c91256418c5907c2f1f8a0ec0a36f9e6cc Mon Sep 17 00:00:00 2001 From: Joe Peterson Date: Fri, 2 Jan 2009 13:40:53 +0000 Subject: n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/n_tty.c | 736 +++++++++++++++++++++++++++++++++++++++----------- drivers/char/tty_io.c | 6 +- drivers/char/vt.c | 2 +- include/linux/tty.h | 6 + 4 files changed, 594 insertions(+), 156 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index efbfe9612658..a9bc5764fe75 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -62,6 +62,17 @@ #define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ #define TTY_THRESHOLD_UNTHROTTLE 128 +/* + * Special byte codes used in the echo buffer to represent operations + * or special handling of characters. Bytes in the echo buffer that + * are not part of such special blocks are treated as normal character + * codes. + */ +#define ECHO_OP_START 0xff +#define ECHO_OP_MOVE_BACK_COL 0x80 +#define ECHO_OP_SET_CANON_COL 0x81 +#define ECHO_OP_ERASE_TAB 0x82 + static inline unsigned char *alloc_buf(void) { gfp_t prio = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; @@ -169,6 +180,7 @@ static void check_unthrottle(struct tty_struct *tty) * * Locking: tty_read_lock for read fields. */ + static void reset_buffer_flags(struct tty_struct *tty) { unsigned long flags; @@ -176,6 +188,11 @@ static void reset_buffer_flags(struct tty_struct *tty) spin_lock_irqsave(&tty->read_lock, flags); tty->read_head = tty->read_tail = tty->read_cnt = 0; spin_unlock_irqrestore(&tty->read_lock, flags); + + mutex_lock(&tty->echo_lock); + tty->echo_pos = tty->echo_cnt = tty->echo_overrun = 0; + mutex_unlock(&tty->echo_lock); + tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); n_tty_set_room(tty); @@ -266,89 +283,116 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty) } /** - * opost - output post processor + * do_output_char - output one character * @c: character (or partial unicode symbol) * @tty: terminal device + * @space: space available in tty driver write buffer * - * Perform OPOST processing. Returns -1 when the output device is - * full and the character must be retried. Note that Linux currently - * ignores TABDLY, CRDLY, VTDLY, FFDLY and NLDLY. They simply aren't - * relevant in the world today. If you ever need them, add them here. + * This is a helper function that handles one output character + * (including special characters like TAB, CR, LF, etc.), + * putting the results in the tty driver's write buffer. + * + * Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY + * and NLDLY. They simply aren't relevant in the world today. + * If you ever need them, add them here. * - * Called from both the receive and transmit sides and can be called - * re-entrantly. Relies on lock_kernel() for tty->column state. + * Returns the number of bytes of buffer space used or -1 if + * no space left. + * + * Locking: should be called under the output_lock to protect + * the column state and space left in the buffer */ -static int opost(unsigned char c, struct tty_struct *tty) +static int do_output_char(unsigned char c, struct tty_struct *tty, int space) { - int space, spaces; + int spaces; - space = tty_write_room(tty); if (!space) return -1; - - lock_kernel(); - if (O_OPOST(tty)) { - switch (c) { - case '\n': - if (O_ONLRET(tty)) - tty->column = 0; - if (O_ONLCR(tty)) { - if (space < 2) { - unlock_kernel(); - return -1; - } - tty_put_char(tty, '\r'); - tty->column = 0; - } - tty->canon_column = tty->column; - break; - case '\r': - if (O_ONOCR(tty) && tty->column == 0) { - unlock_kernel(); - return 0; - } - if (O_OCRNL(tty)) { - c = '\n'; - if (O_ONLRET(tty)) - tty->canon_column = tty->column = 0; - break; - } + + switch (c) { + case '\n': + if (O_ONLRET(tty)) + tty->column = 0; + if (O_ONLCR(tty)) { + if (space < 2) + return -1; tty->canon_column = tty->column = 0; + tty_put_char(tty, '\r'); + tty_put_char(tty, c); + return 2; + } + tty->canon_column = tty->column; + break; + case '\r': + if (O_ONOCR(tty) && tty->column == 0) + return 0; + if (O_OCRNL(tty)) { + c = '\n'; + if (O_ONLRET(tty)) + tty->canon_column = tty->column = 0; break; - case '\t': - spaces = 8 - (tty->column & 7); - if (O_TABDLY(tty) == XTABS) { - if (space < spaces) { - unlock_kernel(); - return -1; - } - tty->column += spaces; - tty->ops->write(tty, " ", spaces); - unlock_kernel(); - return 0; - } + } + tty->canon_column = tty->column = 0; + break; + case '\t': + spaces = 8 - (tty->column & 7); + if (O_TABDLY(tty) == XTABS) { + if (space < spaces) + return -1; tty->column += spaces; - break; - case '\b': - if (tty->column > 0) - tty->column--; - break; - default: - if (O_OLCUC(tty)) - c = toupper(c); - if (!iscntrl(c) && !is_continuation(c, tty)) - tty->column++; - break; + tty->ops->write(tty, " ", spaces); + return spaces; } + tty->column += spaces; + break; + case '\b': + if (tty->column > 0) + tty->column--; + break; + default: + if (O_OLCUC(tty)) + c = toupper(c); + if (!iscntrl(c) && !is_continuation(c, tty)) + tty->column++; + break; } + tty_put_char(tty, c); - unlock_kernel(); - return 0; + return 1; +} + +/** + * process_output - output post processor + * @c: character (or partial unicode symbol) + * @tty: terminal device + * + * Perform OPOST processing. Returns -1 when the output device is + * full and the character must be retried. + * + * Locking: output_lock to protect column state and space left + * (also, this is called from n_tty_write under the + * tty layer write lock) + */ + +static int process_output(unsigned char c, struct tty_struct *tty) +{ + int space, retval; + + mutex_lock(&tty->output_lock); + + space = tty_write_room(tty); + retval = do_output_char(c, tty, space); + + mutex_unlock(&tty->output_lock); + if (retval < 0) + return -1; + else + return 0; } /** - * opost_block - block postprocess + * process_output_block - block post processor * @tty: terminal device * @inbuf: user buffer * @nr: number of bytes @@ -358,24 +402,29 @@ static int opost(unsigned char c, struct tty_struct *tty) * the simple cases normally found and helps to generate blocks of * symbols for the console driver and thus improve performance. * - * Called from n_tty_write under the tty layer write lock. Relies - * on lock_kernel for the tty->column state. + * Locking: output_lock to protect column state and space left + * (also, this is called from n_tty_write under the + * tty layer write lock) */ -static ssize_t opost_block(struct tty_struct *tty, - const unsigned char *buf, unsigned int nr) +static ssize_t process_output_block(struct tty_struct *tty, + const unsigned char *buf, unsigned int nr) { int space; int i; const unsigned char *cp; + mutex_lock(&tty->output_lock); + space = tty_write_room(tty); if (!space) + { + mutex_unlock(&tty->output_lock); return 0; + } if (nr > space) nr = space; - lock_kernel(); for (i = 0, cp = buf; i < nr; i++, cp++) { switch (*cp) { case '\n': @@ -407,46 +456,393 @@ static ssize_t opost_block(struct tty_struct *tty, } } break_out: - if (tty->ops->flush_chars) - tty->ops->flush_chars(tty); i = tty->ops->write(tty, buf, i); - unlock_kernel(); + + mutex_unlock(&tty->output_lock); return i; } +/** + * process_echoes - write pending echo characters + * @tty: terminal device + * + * Write previously buffered echo (and other ldisc-generated) + * characters to the tty. + * + * Characters generated by the ldisc (including echoes) need to + * be buffered because the driver's write buffer can fill during + * heavy program output. Echoing straight to the driver will + * often fail under these conditions, causing lost characters and + * resulting mismatches of ldisc state information. + * + * Since the ldisc state must represent the characters actually sent + * to the driver at the time of the write, operations like certain + * changes in column state are also saved in the buffer and executed + * here. + * + * A circular fifo buffer is used so that the most recent characters + * are prioritized. Also, when control characters are echoed with a + * prefixed "^", the pair is treated atomically and thus not separated. + * + * Locking: output_lock to protect column state and space left, + * echo_lock to protect the echo buffer + */ + +static void process_echoes(struct tty_struct *tty) +{ + int space, nr; + unsigned char c; + unsigned char *cp, *buf_end; + + if (!tty->echo_cnt) + return; + + mutex_lock(&tty->output_lock); + mutex_lock(&tty->echo_lock); + + space = tty_write_room(tty); + + buf_end = tty->echo_buf + N_TTY_BUF_SIZE; + cp = tty->echo_buf + tty->echo_pos; + nr = tty->echo_cnt; + while (nr > 0) { + c = *cp; + if (c == ECHO_OP_START) { + unsigned char op; + unsigned char *opp; + int no_space_left = 0; + + /* + * If the buffer byte is the start of a multi-byte + * operation, get the next byte, which is either the + * op code or a control character value. + */ + opp = cp + 1; + if (opp == buf_end) + opp -= N_TTY_BUF_SIZE; + op = *opp; + + switch (op) { + unsigned int num_chars, num_bs; + + case ECHO_OP_ERASE_TAB: + if (++opp == buf_end) + opp -= N_TTY_BUF_SIZE; + num_chars = *opp; + + /* + * Determine how many columns to go back + * in order to erase the tab. + * This depends on the number of columns + * used by other characters within the tab + * area. If this (modulo 8) count is from + * the start of input rather than from a + * previous tab, we offset by canon column. + * Otherwise, tab spacing is normal. + */ + if (!(num_chars & 0x80)) + num_chars += tty->canon_column; + num_bs = 8 - (num_chars & 7); + + if (num_bs > space) { + no_space_left = 1; + break; + } + space -= num_bs; + while (num_bs--) { + tty_put_char(tty, '\b'); + if (tty->column > 0) + tty->column--; + } + cp += 3; + nr -= 3; + break; + + case ECHO_OP_SET_CANON_COL: + tty->canon_column = tty->column; + cp += 2; + nr -= 2; + break; + + case ECHO_OP_MOVE_BACK_COL: + if (tty->column > 0) + tty->column--; + cp += 2; + nr -= 2; + break; + + case ECHO_OP_START: + /* This is an escaped echo op start code */ + if (!space) { + no_space_left = 1; + break; + } + tty_put_char(tty, ECHO_OP_START); + tty->column++; + space--; + cp += 2; + nr -= 2; + break; + + default: + if (iscntrl(op)) { + if (L_ECHOCTL(tty)) { + /* + * Ensure there is enough space + * for the whole ctrl pair. + */ + if (space < 2) { + no_space_left = 1; + break; + } + tty_put_char(tty, '^'); + tty_put_char(tty, op ^ 0100); + tty->column += 2; + space -= 2; + } else { + if (!space) { + no_space_left = 1; + break; + } + tty_put_char(tty, op); + space--; + } + } + /* + * If above falls through, this was an + * undefined op. + */ + cp += 2; + nr -= 2; + } + + if (no_space_left) + break; + } else { + int retval; + + if ((retval = do_output_char(c, tty, space)) < 0) + break; + space -= retval; + cp += 1; + nr -= 1; + } + + /* When end of circular buffer reached, wrap around */ + if (cp >= buf_end) + cp -= N_TTY_BUF_SIZE; + } + + if (nr == 0) { + tty->echo_pos = 0; + tty->echo_cnt = 0; + tty->echo_overrun = 0; + } else { + int num_processed = tty->echo_cnt - nr; + tty->echo_pos += num_processed; + tty->echo_pos &= N_TTY_BUF_SIZE - 1; + tty->echo_cnt = nr; + if (num_processed > 0) + tty->echo_overrun = 0; + } + + mutex_unlock(&tty->echo_lock); + mutex_unlock(&tty->output_lock); + + if (tty->ops->flush_chars) + tty->ops->flush_chars(tty); +} + +/** + * add_echo_byte - add a byte to the echo buffer + * @c: unicode byte to echo + * @tty: terminal device + * + * Add a character or operation byte to the echo buffer. + * + * Should be called under the echo lock to protect the echo buffer. + */ + +static void add_echo_byte(unsigned char c, struct tty_struct *tty) +{ + int new_byte_pos; + + if (tty->echo_cnt == N_TTY_BUF_SIZE) { + /* Circular buffer is already at capacity */ + new_byte_pos = tty->echo_pos; + + /* + * Since the buffer start position needs to be advanced, + * be sure to step by a whole operation byte group. + */ + if (tty->echo_buf[tty->echo_pos] == ECHO_OP_START) + { + if (tty->echo_buf[(tty->echo_pos + 1) & + (N_TTY_BUF_SIZE - 1)] == + ECHO_OP_ERASE_TAB) { + tty->echo_pos += 3; + tty->echo_cnt -= 2; + } else { + tty->echo_pos += 2; + tty->echo_cnt -= 1; + } + } else { + tty->echo_pos++; + } + tty->echo_pos &= N_TTY_BUF_SIZE - 1; + + tty->echo_overrun = 1; + } else { + new_byte_pos = tty->echo_pos + tty->echo_cnt; + new_byte_pos &= N_TTY_BUF_SIZE - 1; + tty->echo_cnt++; + } + + tty->echo_buf[new_byte_pos] = c; +} + +/** + * echo_move_back_col - add operation to move back a column + * @tty: terminal device + * + * Add an operation to the echo buffer to move back one column. + * + * Locking: echo_lock to protect the echo buffer + */ + +static void echo_move_back_col(struct tty_struct *tty) +{ + mutex_lock(&tty->echo_lock); + + add_echo_byte(ECHO_OP_START, tty); + add_echo_byte(ECHO_OP_MOVE_BACK_COL, tty); + + mutex_unlock(&tty->echo_lock); +} + +/** + * echo_set_canon_col - add operation to set the canon column + * @tty: terminal device + * + * Add an operation to the echo buffer to set the canon column + * to the current column. + * + * Locking: echo_lock to protect the echo buffer + */ + +static void echo_set_canon_col(struct tty_struct *tty) +{ + mutex_lock(&tty->echo_lock); + + add_echo_byte(ECHO_OP_START, tty); + add_echo_byte(ECHO_OP_SET_CANON_COL, tty); + + mutex_unlock(&tty->echo_lock); +} + +/** + * echo_erase_tab - add operation to erase a tab + * @num_chars: number of character columns already used + * @after_tab: true if num_chars starts after a previous tab + * @tty: terminal device + * + * Add an operation to the echo buffer to erase a tab. + * + * Called by the eraser function, which knows how many character + * columns have been used since either a previous tab or the start + * of input. This information will be used later, along with + * canon column (if applicable), to go back the correct number + * of columns. + * + * Locking: echo_lock to protect the echo buffer + */ + +static void echo_erase_tab(unsigned int num_chars, int after_tab, + struct tty_struct *tty) +{ + mutex_lock(&tty->echo_lock); + + add_echo_byte(ECHO_OP_START, tty); + add_echo_byte(ECHO_OP_ERASE_TAB, tty); + + /* We only need to know this modulo 8 (tab spacing) */ + num_chars &= 7; + + /* Set the high bit as a flag if num_chars is after a previous tab */ + if (after_tab) + num_chars |= 0x80; + + add_echo_byte(num_chars, tty); + + mutex_unlock(&tty->echo_lock); +} + +/** + * echo_char_raw - echo a character raw + * @c: unicode byte to echo + * @tty: terminal device + * + * Echo user input back onto the screen. This must be called only when + * L_ECHO(tty) is true. Called from the driver receive_buf path. + * + * This variant does not treat control characters specially. + * + * Locking: echo_lock to protect the echo buffer + */ + +static void echo_char_raw(unsigned char c, struct tty_struct *tty) +{ + mutex_lock(&tty->echo_lock); + + if (c == ECHO_OP_START) { + add_echo_byte(ECHO_OP_START, tty); + add_echo_byte(ECHO_OP_START, tty); + } else { + add_echo_byte(c, tty); + } + + mutex_unlock(&tty->echo_lock); +} /** - * echo_char - echo characters + * echo_char - echo a character * @c: unicode byte to echo * @tty: terminal device * * Echo user input back onto the screen. This must be called only when * L_ECHO(tty) is true. Called from the driver receive_buf path. * - * Relies on BKL for tty column locking + * This variant tags control characters to be possibly echoed as + * as "^X" (where X is the letter representing the control char). + * + * Locking: echo_lock to protect the echo buffer */ static void echo_char(unsigned char c, struct tty_struct *tty) { - if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') { - tty_put_char(tty, '^'); - tty_put_char(tty, c ^ 0100); - tty->column += 2; - } else - opost(c, tty); + mutex_lock(&tty->echo_lock); + + if (c == ECHO_OP_START) { + add_echo_byte(ECHO_OP_START, tty); + add_echo_byte(ECHO_OP_START, tty); + } else { + if (iscntrl(c) && c != '\t') + add_echo_byte(ECHO_OP_START, tty); + add_echo_byte(c, tty); + } + + mutex_unlock(&tty->echo_lock); } /** - * finsh_erasing - complete erase + * finish_erasing - complete erase * @tty: tty doing the erase - * - * Relies on BKL for tty column locking */ + static inline void finish_erasing(struct tty_struct *tty) { if (tty->erasing) { - tty_put_char(tty, '/'); - tty->column++; + echo_char_raw('/', tty); tty->erasing = 0; } } @@ -460,7 +856,7 @@ static inline void finish_erasing(struct tty_struct *tty) * present in the stream from the driver layer. Handles the complexities * of UTF-8 multibyte symbols. * - * Locking: read_lock for tty buffers, BKL for column/erasing state + * Locking: read_lock for tty buffers */ static void eraser(unsigned char c, struct tty_struct *tty) @@ -471,7 +867,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) /* FIXME: locking needed ? */ if (tty->read_head == tty->canon_head) { - /* opost('\a', tty); */ /* what do you think? */ + /* echo_char_raw('\a', tty); */ /* what do you think? */ return; } if (c == ERASE_CHAR(tty)) @@ -497,7 +893,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ if (L_ECHOK(tty)) - opost('\n', tty); + echo_char_raw('\n', tty); return; } kill_type = KILL; @@ -533,67 +929,62 @@ static void eraser(unsigned char c, struct tty_struct *tty) if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!tty->erasing) { - tty_put_char(tty, '\\'); - tty->column++; + echo_char_raw('\\', tty); tty->erasing = 1; } /* if cnt > 1, output a multi-byte character */ echo_char(c, tty); while (--cnt > 0) { head = (head+1) & (N_TTY_BUF_SIZE-1); - tty_put_char(tty, tty->read_buf[head]); + echo_char_raw(tty->read_buf[head], tty); + echo_move_back_col(tty); } } else if (kill_type == ERASE && !L_ECHOE(tty)) { echo_char(ERASE_CHAR(tty), tty); } else if (c == '\t') { - unsigned int col = tty->canon_column; - unsigned long tail = tty->canon_head; - - /* Find the column of the last char. */ - while (tail != tty->read_head) { + unsigned int num_chars = 0; + int after_tab = 0; + unsigned long tail = tty->read_head; + + /* + * Count the columns used for characters + * since the start of input or after a + * previous tab. + * This info is used to go back the correct + * number of columns. + */ + while (tail != tty->canon_head) { + tail = (tail-1) & (N_TTY_BUF_SIZE-1); c = tty->read_buf[tail]; - if (c == '\t') - col = (col | 7) + 1; + if (c == '\t') { + after_tab = 1; + break; + } else if (iscntrl(c)) { if (L_ECHOCTL(tty)) - col += 2; - } else if (!is_continuation(c, tty)) - col++; - tail = (tail+1) & (N_TTY_BUF_SIZE-1); - } - - /* should never happen */ - if (tty->column > 0x80000000) - tty->column = 0; - - /* Now backup to that column. */ - while (tty->column > col) { - /* Can't use opost here. */ - tty_put_char(tty, '\b'); - if (tty->column > 0) - tty->column--; + num_chars += 2; + } else if (!is_continuation(c, tty)) { + num_chars++; + } } + echo_erase_tab(num_chars, after_tab, tty); } else { if (iscntrl(c) && L_ECHOCTL(tty)) { - tty_put_char(tty, '\b'); - tty_put_char(tty, ' '); - tty_put_char(tty, '\b'); - if (tty->column > 0) - tty->column--; + echo_char_raw('\b', tty); + echo_char_raw(' ', tty); + echo_char_raw('\b', tty); } if (!iscntrl(c) || L_ECHOCTL(tty)) { - tty_put_char(tty, '\b'); - tty_put_char(tty, ' '); - tty_put_char(tty, '\b'); - if (tty->column > 0) - tty->column--; + echo_char_raw('\b', tty); + echo_char_raw(' ', tty); + echo_char_raw('\b', tty); } } } if (kill_type == ERASE) break; } - if (tty->read_head == tty->canon_head) + if (tty->read_head == tty->canon_head && L_ECHO(tty)) finish_erasing(tty); } @@ -724,14 +1115,18 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) c=tolower(c); if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && - ((I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty)) || - c == INTR_CHAR(tty) || c == QUIT_CHAR(tty) || c == SUSP_CHAR(tty))) + I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) && + c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) { start_tty(tty); + process_echoes(tty); + } if (tty->closing) { if (I_IXON(tty)) { - if (c == START_CHAR(tty)) + if (c == START_CHAR(tty)) { start_tty(tty); + process_echoes(tty); + } else if (c == STOP_CHAR(tty)) stop_tty(tty); } @@ -745,17 +1140,20 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) * up. */ if (!test_bit(c, tty->process_char_map) || tty->lnext) { - finish_erasing(tty); tty->lnext = 0; if (L_ECHO(tty)) { + finish_erasing(tty); if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { - tty_put_char(tty, '\a'); /* beep if no space */ + /* beep if no space */ + echo_char_raw('\a', tty); + process_echoes(tty); return; } /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) - tty->canon_column = tty->column; + echo_set_canon_col(tty); echo_char(c, tty); + process_echoes(tty); } if (I_PARMRK(tty) && c == (unsigned char) '\377') put_tty_queue(c, tty); @@ -766,6 +1164,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) if (I_IXON(tty)) { if (c == START_CHAR(tty)) { start_tty(tty); + process_echoes(tty); return; } if (c == STOP_CHAR(tty)) { @@ -786,7 +1185,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) if (c == SUSP_CHAR(tty)) { send_signal: /* - * Echo character, and then send the signal. * Note that we do not use isig() here because we want * the order to be: * 1) flush, 2) echo, 3) signal @@ -795,8 +1193,12 @@ send_signal: n_tty_flush_buffer(tty); tty_driver_flush_buffer(tty); } - if (L_ECHO(tty)) + if (I_IXON(tty)) + start_tty(tty); + if (L_ECHO(tty)) { echo_char(c, tty); + process_echoes(tty); + } if (tty->pgrp) kill_pgrp(tty->pgrp, signal, 1); return; @@ -815,6 +1217,7 @@ send_signal: if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); + process_echoes(tty); return; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { @@ -822,8 +1225,9 @@ send_signal: if (L_ECHO(tty)) { finish_erasing(tty); if (L_ECHOCTL(tty)) { - tty_put_char(tty, '^'); - tty_put_char(tty, '\b'); + echo_char_raw('^', tty); + echo_char_raw('\b', tty); + process_echoes(tty); } } return; @@ -834,18 +1238,20 @@ send_signal: finish_erasing(tty); echo_char(c, tty); - opost('\n', tty); + echo_char_raw('\n', tty); while (tail != tty->read_head) { echo_char(tty->read_buf[tail], tty); tail = (tail+1) & (N_TTY_BUF_SIZE-1); } + process_echoes(tty); return; } if (c == '\n') { if (L_ECHO(tty) || L_ECHONL(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) - tty_put_char(tty, '\a'); - opost('\n', tty); + echo_char_raw('\a', tty); + echo_char_raw('\n', tty); + process_echoes(tty); } goto handle_newline; } @@ -862,11 +1268,12 @@ send_signal: */ if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) - tty_put_char(tty, '\a'); + echo_char_raw('\a', tty); /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) - tty->canon_column = tty->column; + echo_set_canon_col(tty); echo_char(c, tty); + process_echoes(tty); } /* * XXX does PARMRK doubling happen for @@ -889,20 +1296,23 @@ handle_newline: } } - finish_erasing(tty); if (L_ECHO(tty)) { + finish_erasing(tty); if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { - tty_put_char(tty, '\a'); /* beep if no space */ + /* beep if no space */ + echo_char_raw('\a', tty); + process_echoes(tty); return; } if (c == '\n') - opost('\n', tty); + echo_char_raw('\n', tty); else { /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) - tty->canon_column = tty->column; + echo_set_canon_col(tty); echo_char(c, tty); } + process_echoes(tty); } if (I_PARMRK(tty) && c == (unsigned char) '\377') @@ -923,6 +1333,9 @@ handle_newline: static void n_tty_write_wakeup(struct tty_struct *tty) { + /* Write out any echoed characters that are still pending */ + process_echoes(tty); + if (tty->fasync) { set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); kill_fasync(&tty->fasync, SIGIO, POLL_OUT); @@ -1134,6 +1547,10 @@ static void n_tty_close(struct tty_struct *tty) free_buf(tty->read_buf); tty->read_buf = NULL; } + if (tty->echo_buf) { + free_buf(tty->echo_buf); + tty->echo_buf = NULL; + } } /** @@ -1151,13 +1568,19 @@ static int n_tty_open(struct tty_struct *tty) if (!tty) return -EINVAL; - /* This one is ugly. Currently a malloc failure here can panic */ + /* These are ugly. Currently a malloc failure here can panic */ if (!tty->read_buf) { tty->read_buf = alloc_buf(); if (!tty->read_buf) return -ENOMEM; } + if (!tty->echo_buf) { + tty->echo_buf = alloc_buf(); + if (!tty->echo_buf) + return -ENOMEM; + } memset(tty->read_buf, 0, N_TTY_BUF_SIZE); + memset(tty->echo_buf, 0, N_TTY_BUF_SIZE); reset_buffer_flags(tty); tty->column = 0; n_tty_set_termios(tty, NULL); @@ -1487,16 +1910,23 @@ do_it_again: * @buf: userspace buffer pointer * @nr: size of I/O * - * Write function of the terminal device. This is serialized with + * Write function of the terminal device. This is serialized with * respect to other write callers but not to termios changes, reads - * and other such events. We must be careful with N_TTY as the receive - * code will echo characters, thus calling driver write methods. + * and other such events. Since the receive code will echo characters, + * thus calling driver write methods, the output_lock is used in + * the output processing functions called here as well as in the + * echo processing function to protect the column state and space + * left in the buffer. * * This code must be sure never to sleep through a hangup. + * + * Locking: output_lock to protect column state and space left + * (note that the process_output*() functions take this + * lock themselves) */ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, - const unsigned char *buf, size_t nr) + const unsigned char *buf, size_t nr) { const unsigned char *b = buf; DECLARE_WAITQUEUE(wait, current); @@ -1510,6 +1940,9 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, return retval; } + /* Write out any echoed characters that are still pending */ + process_echoes(tty); + add_wait_queue(&tty->write_wait, &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); @@ -1523,7 +1956,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, } if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { while (nr > 0) { - ssize_t num = opost_block(tty, b, nr); + ssize_t num = process_output_block(tty, b, nr); if (num < 0) { if (num == -EAGAIN) break; @@ -1535,7 +1968,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, if (nr == 0) break; c = *b; - if (opost(c, tty) < 0) + if (process_output(c, tty) < 0) break; b++; nr--; } @@ -1663,4 +2096,3 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .receive_buf = n_tty_receive_buf, .write_wakeup = n_tty_write_wakeup }; - diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index db15f9ba7c0b..d8d240c8a25a 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1111,9 +1111,7 @@ void tty_write_message(struct tty_struct *tty, char *msg) * Locks the line discipline as required * Writes to the tty driver are serialized by the atomic_write_lock * and are then processed in chunks to the device. The line discipline - * write method will not be involked in parallel for each device - * The line discipline write method is called under the big - * kernel lock for historical reasons. New code should not rely on this. + * write method will not be invoked in parallel for each device. */ static ssize_t tty_write(struct file *file, const char __user *buf, @@ -2785,6 +2783,8 @@ void initialize_tty_struct(struct tty_struct *tty, INIT_WORK(&tty->hangup_work, do_tty_hangup); mutex_init(&tty->atomic_read_lock); mutex_init(&tty->atomic_write_lock); + mutex_init(&tty->output_lock); + mutex_init(&tty->echo_lock); spin_lock_init(&tty->read_lock); spin_lock_init(&tty->ctrl_lock); INIT_LIST_HEAD(&tty->tty_files); diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 008176edbd64..639e126b2bff 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2679,7 +2679,7 @@ static int con_write_room(struct tty_struct *tty) { if (tty->stopped) return 0; - return 4096; /* No limit, really; we're not buffering */ + return 32768; /* No limit, really; we're not buffering */ } static int con_chars_in_buffer(struct tty_struct *tty) diff --git a/include/linux/tty.h b/include/linux/tty.h index 3f4954c55e53..dfc77ded198a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -253,6 +253,7 @@ struct tty_struct { unsigned int column; unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; unsigned char closing:1; + unsigned char echo_overrun:1; unsigned short minimum_to_wake; unsigned long overrun_time; int num_overrun; @@ -262,11 +263,16 @@ struct tty_struct { int read_tail; int read_cnt; unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))]; + unsigned char *echo_buf; + unsigned int echo_pos; + unsigned int echo_cnt; int canon_data; unsigned long canon_head; unsigned int canon_column; struct mutex atomic_read_lock; struct mutex atomic_write_lock; + struct mutex output_lock; + struct mutex echo_lock; unsigned char *write_buf; int write_cnt; spinlock_t read_lock; -- cgit v1.2.3 From fc6f6238226e6d1248e1967eae2bf556eaf3ac17 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 2 Jan 2009 13:43:17 +0000 Subject: pty: simplify resize We have special case logic for resizing pty/tty pairs. We also have a per driver resize method so for the pty case we should use it. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/hvc_console.c | 2 +- drivers/char/pty.c | 54 +++++++++++++++++++++++++++++++++++++++++++++- drivers/char/tty_io.c | 31 ++++++++++---------------- drivers/char/vt.c | 14 ++++++------ include/linux/tty.h | 3 +-- include/linux/tty_driver.h | 6 ++---- 6 files changed, 74 insertions(+), 36 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 0587b66d6fc7..5a8a4c28c867 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -529,7 +529,7 @@ static void hvc_set_winsz(struct work_struct *work) tty = tty_kref_get(hp->tty); spin_unlock_irqrestore(&hp->lock, hvc_flags); - tty_do_resize(tty, tty, &ws); + tty_do_resize(tty, &ws); tty_kref_put(tty); } diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 6d4582712b1f..b5daaaa9007e 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -230,6 +230,55 @@ static void pty_set_termios(struct tty_struct *tty, tty->termios->c_cflag |= (CS8 | CREAD); } +/** + * pty_do_resize - resize event + * @tty: tty being resized + * @real_tty: real tty (not the same as tty if using a pty/tty pair) + * @rows: rows (character) + * @cols: cols (character) + * + * Update the termios variables and send the neccessary signals to + * peform a terminal resize correctly + */ + +int pty_resize(struct tty_struct *tty, struct winsize *ws) +{ + struct pid *pgrp, *rpgrp; + unsigned long flags; + struct tty_struct *pty = tty->link; + + /* For a PTY we need to lock the tty side */ + mutex_lock(&tty->termios_mutex); + if (!memcmp(ws, &tty->winsize, sizeof(*ws))) + goto done; + + /* Get the PID values and reference them so we can + avoid holding the tty ctrl lock while sending signals. + We need to lock these individually however. */ + + spin_lock_irqsave(&tty->ctrl_lock, flags); + pgrp = get_pid(tty->pgrp); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + + spin_lock_irqsave(&pty->ctrl_lock, flags); + rpgrp = get_pid(pty->pgrp); + spin_unlock_irqrestore(&pty->ctrl_lock, flags); + + if (pgrp) + kill_pgrp(pgrp, SIGWINCH, 1); + if (rpgrp != pgrp && rpgrp) + kill_pgrp(rpgrp, SIGWINCH, 1); + + put_pid(pgrp); + put_pid(rpgrp); + + tty->winsize = *ws; + pty->winsize = *ws; /* Never used so will go away soon */ +done: + mutex_unlock(&tty->termios_mutex); + return 0; +} + static int pty_install(struct tty_driver *driver, struct tty_struct *tty) { struct tty_struct *o_tty; @@ -290,6 +339,7 @@ static const struct tty_operations pty_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, + .resize = pty_resize }; /* Traditional BSD devices */ @@ -319,6 +369,7 @@ static const struct tty_operations pty_ops_bsd = { .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, .ioctl = pty_bsd_ioctl, + .resize = pty_resize }; static void __init legacy_pty_init(void) @@ -561,7 +612,8 @@ static const struct tty_operations ptm_unix98_ops = { .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, .ioctl = pty_unix98_ioctl, - .shutdown = pty_unix98_shutdown + .shutdown = pty_unix98_shutdown, + .resize = pty_resize }; static const struct tty_operations pty_unix98_ops = { diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 2a15af65dd11..d33e5ab06177 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2048,7 +2048,6 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) /** * tty_do_resize - resize event * @tty: tty being resized - * @real_tty: real tty (not the same as tty if using a pty/tty pair) * @rows: rows (character) * @cols: cols (character) * @@ -2056,41 +2055,34 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) * peform a terminal resize correctly */ -int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, - struct winsize *ws) +int tty_do_resize(struct tty_struct *tty, struct winsize *ws) { - struct pid *pgrp, *rpgrp; + struct pid *pgrp; unsigned long flags; - /* For a PTY we need to lock the tty side */ - mutex_lock(&real_tty->termios_mutex); - if (!memcmp(ws, &real_tty->winsize, sizeof(*ws))) + /* Lock the tty */ + mutex_lock(&tty->termios_mutex); + if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; /* Get the PID values and reference them so we can avoid holding the tty ctrl lock while sending signals */ spin_lock_irqsave(&tty->ctrl_lock, flags); pgrp = get_pid(tty->pgrp); - rpgrp = get_pid(real_tty->pgrp); spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (pgrp) kill_pgrp(pgrp, SIGWINCH, 1); - if (rpgrp != pgrp && rpgrp) - kill_pgrp(rpgrp, SIGWINCH, 1); - put_pid(pgrp); - put_pid(rpgrp); tty->winsize = *ws; - real_tty->winsize = *ws; done: - mutex_unlock(&real_tty->termios_mutex); + mutex_unlock(&tty->termios_mutex); return 0; } /** * tiocswinsz - implement window size set ioctl - * @tty; tty + * @tty; tty side of tty * @arg: user buffer for result * * Copies the user idea of the window size to the kernel. Traditionally @@ -2103,17 +2095,16 @@ done: * then calls into the default method. */ -static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, - struct winsize __user *arg) +static int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg) { struct winsize tmp_ws; if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) return -EFAULT; if (tty->ops->resize) - return tty->ops->resize(tty, real_tty, &tmp_ws); + return tty->ops->resize(tty, &tmp_ws); else - return tty_do_resize(tty, real_tty, &tmp_ws); + return tty_do_resize(tty, &tmp_ws); } /** @@ -2538,7 +2529,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCGWINSZ: return tiocgwinsz(real_tty, p); case TIOCSWINSZ: - return tiocswinsz(tty, real_tty, p); + return tiocswinsz(real_tty, p); case TIOCCONS: return real_tty != tty ? -EINVAL : tioccons(file); case FIONBIO: diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 639e126b2bff..80014213fb53 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -819,8 +819,8 @@ static inline int resize_screen(struct vc_data *vc, int width, int height, * ctrl_lock of the tty IFF a tty is passed. */ -static int vc_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, - struct vc_data *vc, unsigned int cols, unsigned int lines) +static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, + unsigned int cols, unsigned int lines) { unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; unsigned int old_cols, old_rows, old_row_size, old_screen_size; @@ -932,7 +932,7 @@ static int vc_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, ws.ws_row = vc->vc_rows; ws.ws_col = vc->vc_cols; ws.ws_ypixel = vc->vc_scan_lines; - tty_do_resize(tty, real_tty, &ws); + tty_do_resize(tty, &ws); } if (CON_IS_VISIBLE(vc)) @@ -954,13 +954,12 @@ static int vc_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) { - return vc_do_resize(vc->vc_tty, vc->vc_tty, vc, cols, rows); + return vc_do_resize(vc->vc_tty, vc, cols, rows); } /** * vt_resize - resize a VT * @tty: tty to resize - * @real_tty: tty if a pty/tty pair * @ws: winsize attributes * * Resize a virtual terminal. This is called by the tty layer as we @@ -971,14 +970,13 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) * termios_mutex and the tty ctrl_lock in that order. */ -int vt_resize(struct tty_struct *tty, struct tty_struct *real_tty, - struct winsize *ws) +int vt_resize(struct tty_struct *tty, struct winsize *ws) { struct vc_data *vc = tty->driver_data; int ret; acquire_console_sem(); - ret = vc_do_resize(tty, real_tty, vc, ws->ws_col, ws->ws_row); + ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row); release_console_sem(); return ret; } diff --git a/include/linux/tty.h b/include/linux/tty.h index dfc77ded198a..f88169787a5f 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -360,8 +360,7 @@ extern int tty_write_room(struct tty_struct *tty); extern void tty_driver_flush_buffer(struct tty_struct *tty); extern void tty_throttle(struct tty_struct *tty); extern void tty_unthrottle(struct tty_struct *tty); -extern int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, - struct winsize *ws); +extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws); extern void tty_shutdown(struct tty_struct *tty); extern void tty_free_termios(struct tty_struct *tty); extern int is_current_pgrp_orphaned(void); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 78416b901589..08e088334dba 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -196,8 +196,7 @@ * Optional: If not provided then the write method is called under * the atomic write lock to keep it serialized with the ldisc. * - * int (*resize)(struct tty_struct *tty, struct tty_struct *real_tty, - * unsigned int rows, unsigned int cols); + * int (*resize)(struct tty_struct *tty, struct winsize *ws) * * Called when a termios request is issued which changes the * requested terminal geometry. @@ -258,8 +257,7 @@ struct tty_operations { int (*tiocmget)(struct tty_struct *tty, struct file *file); int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); - int (*resize)(struct tty_struct *tty, struct tty_struct *real_tty, - struct winsize *ws); + int (*resize)(struct tty_struct *tty, struct winsize *ws); int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew); #ifdef CONFIG_CONSOLE_POLL int (*poll_init)(struct tty_driver *driver, int line, char *options); -- cgit v1.2.3 From 975a1a7d887048d4afc9201383e11b7af991866b Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 2 Jan 2009 13:44:27 +0000 Subject: And here's a patch (to be applied on top of the last) which prevents this happening again by making use of 'const'. Signed-off-by: Russell King Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/8250_pci.c | 37 +++++++++++++++++++++---------------- include/linux/8250_pci.h | 2 +- 2 files changed, 22 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 057b532ccaad..0b794138f686 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -42,7 +42,8 @@ struct pci_serial_quirk { u32 subvendor; u32 subdevice; int (*init)(struct pci_dev *dev); - int (*setup)(struct serial_private *, struct pciserial_board *, + int (*setup)(struct serial_private *, + const struct pciserial_board *, struct uart_port *, int); void (*exit)(struct pci_dev *dev); }; @@ -107,7 +108,7 @@ setup_port(struct serial_private *priv, struct uart_port *port, * ADDI-DATA GmbH communication cards */ static int addidata_apci7800_setup(struct serial_private *priv, - struct pciserial_board *board, + const struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar = 0, offset = board->first_offset; @@ -134,7 +135,7 @@ static int addidata_apci7800_setup(struct serial_private *priv, * Not that ugly ;) -- HW */ static int -afavlab_setup(struct serial_private *priv, struct pciserial_board *board, +afavlab_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -188,8 +189,9 @@ static int pci_hp_diva_init(struct pci_dev *dev) * some serial ports are supposed to be hidden on certain models. */ static int -pci_hp_diva_setup(struct serial_private *priv, struct pciserial_board *board, - struct uart_port *port, int idx) +pci_hp_diva_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) { unsigned int offset = board->first_offset; unsigned int bar = FL_GET_BASE(board->flags); @@ -306,7 +308,7 @@ static void __devexit pci_plx9050_exit(struct pci_dev *dev) /* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */ static int -sbs_setup(struct serial_private *priv, struct pciserial_board *board, +sbs_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -463,7 +465,7 @@ static int pci_siig_init(struct pci_dev *dev) } static int pci_siig_setup(struct serial_private *priv, - struct pciserial_board *board, + const struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0; @@ -534,7 +536,8 @@ static int pci_timedia_init(struct pci_dev *dev) * Ugh, this is ugly as all hell --- TYT */ static int -pci_timedia_setup(struct serial_private *priv, struct pciserial_board *board, +pci_timedia_setup(struct serial_private *priv, + const struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar = 0, offset = board->first_offset; @@ -568,7 +571,7 @@ pci_timedia_setup(struct serial_private *priv, struct pciserial_board *board, */ static int titan_400l_800l_setup(struct serial_private *priv, - struct pciserial_board *board, + const struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -770,7 +773,8 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev) } static int -pci_default_setup(struct serial_private *priv, struct pciserial_board *board, +pci_default_setup(struct serial_private *priv, + const struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar, offset = board->first_offset, maxnr; @@ -1099,7 +1103,7 @@ static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) } static inline int get_pci_irq(struct pci_dev *dev, - struct pciserial_board *board) + const struct pciserial_board *board) { if (board->flags & FL_NOIRQ) return 0; @@ -1894,8 +1898,8 @@ serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board) } static inline int -serial_pci_matches(struct pciserial_board *board, - struct pciserial_board *guessed) +serial_pci_matches(const struct pciserial_board *board, + const struct pciserial_board *guessed) { return board->num_ports == guessed->num_ports && @@ -1906,7 +1910,7 @@ serial_pci_matches(struct pciserial_board *board, } struct serial_private * -pciserial_init_ports(struct pci_dev *dev, struct pciserial_board *board) +pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) { struct uart_port serial_port; struct serial_private *priv; @@ -2039,7 +2043,8 @@ static int __devinit pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { struct serial_private *priv; - struct pciserial_board *board, tmp; + const struct pciserial_board *board; + struct pciserial_board tmp; int rc; if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { @@ -2066,7 +2071,7 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) * We matched one of our class entries. Try to * determine the parameters of this board. */ - rc = serial_pci_guess_board(dev, board); + rc = serial_pci_guess_board(dev, &tmp); if (rc) goto disable; } else { diff --git a/include/linux/8250_pci.h b/include/linux/8250_pci.h index 3209dd46ea7d..b24ff086a662 100644 --- a/include/linux/8250_pci.h +++ b/include/linux/8250_pci.h @@ -31,7 +31,7 @@ struct pciserial_board { struct serial_private; struct serial_private * -pciserial_init_ports(struct pci_dev *dev, struct pciserial_board *board); +pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board); void pciserial_remove_ports(struct serial_private *priv); void pciserial_suspend_ports(struct serial_private *priv); void pciserial_resume_ports(struct serial_private *priv); -- cgit v1.2.3 From c9b3976e3fec266be25c5001a70aa0a890b6c476 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 2 Jan 2009 13:44:56 +0000 Subject: tty: Fix PPP hang under load Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/tty_ldisc.c | 30 +++++++++++++++++++++--------- include/linux/tty.h | 1 + 2 files changed, 22 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index f307f135cbfb..7a84b406a952 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c @@ -316,8 +316,7 @@ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) { /* wait_event is a macro */ wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); - if (tty->ldisc.refcount == 0) - printk(KERN_ERR "tty_ldisc_ref_wait\n"); + WARN_ON(tty->ldisc.refcount == 0); return &tty->ldisc; } @@ -376,15 +375,17 @@ EXPORT_SYMBOL_GPL(tty_ldisc_deref); * @tty: terminal to activate ldisc on * * Set the TTY_LDISC flag when the line discipline can be called - * again. Do necessary wakeups for existing sleepers. + * again. Do necessary wakeups for existing sleepers. Clear the LDISC + * changing flag to indicate any ldisc change is now over. * - * Note: nobody should set this bit except via this function. Clearing - * directly is allowed. + * Note: nobody should set the TTY_LDISC bit except via this function. + * Clearing directly is allowed. */ void tty_ldisc_enable(struct tty_struct *tty) { set_bit(TTY_LDISC, &tty->flags); + clear_bit(TTY_LDISC_CHANGING, &tty->flags); wake_up(&tty_ldisc_wait); } @@ -496,7 +497,14 @@ restart: * reference to the line discipline. The TTY_LDISC bit * prevents anyone taking a reference once it is clear. * We need the lock to avoid racing reference takers. + * + * We must clear the TTY_LDISC bit here to avoid a livelock + * with a userspace app continually trying to use the tty in + * parallel to the change and re-referencing the tty. */ + clear_bit(TTY_LDISC, &tty->flags); + if (o_tty) + clear_bit(TTY_LDISC, &o_tty->flags); spin_lock_irqsave(&tty_ldisc_lock, flags); if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) { @@ -528,7 +536,7 @@ restart: * If the TTY_LDISC bit is set, then we are racing against * another ldisc change */ - if (!test_bit(TTY_LDISC, &tty->flags)) { + if (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { struct tty_ldisc *ld; spin_unlock_irqrestore(&tty_ldisc_lock, flags); tty_ldisc_put(new_ldisc.ops); @@ -536,10 +544,14 @@ restart: tty_ldisc_deref(ld); goto restart; } - - clear_bit(TTY_LDISC, &tty->flags); + /* + * This flag is used to avoid two parallel ldisc changes. Once + * open and close are fine grained locked this may work better + * as a mutex shared with the open/close/hup paths + */ + set_bit(TTY_LDISC_CHANGING, &tty->flags); if (o_tty) - clear_bit(TTY_LDISC, &o_tty->flags); + set_bit(TTY_LDISC_CHANGING, &o_tty->flags); spin_unlock_irqrestore(&tty_ldisc_lock, flags); /* diff --git a/include/linux/tty.h b/include/linux/tty.h index f88169787a5f..bbbeaef99626 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -301,6 +301,7 @@ struct tty_struct { #define TTY_PUSH 6 /* n_tty private */ #define TTY_CLOSING 7 /* ->close() in progress */ #define TTY_LDISC 9 /* Line discipline attached */ +#define TTY_LDISC_CHANGING 10 /* Line discipline changing */ #define TTY_HW_COOK_OUT 14 /* Hardware can do output cooking */ #define TTY_HW_COOK_IN 15 /* Hardware can do input cooking */ #define TTY_PTY_LOCK 16 /* pty private */ -- cgit v1.2.3 From 31f35939d1d9bcfb3099b32c67b896d2792603f9 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 2 Jan 2009 13:45:05 +0000 Subject: tty_port: Add a port level carrier detect operation This is the first step to generalising the various pieces of waiting logic duplicated in all sorts of serial drivers. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/esp.c | 61 +++++++++++++++++++++++---------------- drivers/char/generic_serial.c | 43 ++++++++++++++-------------- drivers/char/isicom.c | 51 +++++++++++++++++++++------------ drivers/char/istallion.c | 28 ++++++++++++------ drivers/char/moxa.c | 26 +++++++++++++---- drivers/char/mxser.c | 54 +++++++++++++++++++++-------------- drivers/char/rio/rio_linux.c | 18 ++++++------ drivers/char/riscom8.c | 65 ++++++++++++++++++++++++++---------------- drivers/char/rocket.c | 40 +++++++++++++++++--------- drivers/char/ser_a2232.c | 19 ++++++------ drivers/char/stallion.c | 28 +++++++++++++----- drivers/char/sx.c | 27 +++++++++--------- drivers/char/synclink.c | 61 ++++++++++++++++++++++++++------------- drivers/char/synclink_gt.c | 48 ++++++++++++++++++++----------- drivers/char/synclinkmp.c | 55 ++++++++++++++++++++++------------- drivers/char/tty_port.c | 17 +++++++++++ drivers/char/vme_scc.c | 15 ++++++---- include/linux/generic_serial.h | 1 - include/linux/tty.h | 9 ++++++ 19 files changed, 427 insertions(+), 239 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 7f077c0097f6..45ec263ec012 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -2054,6 +2054,15 @@ static void esp_hangup(struct tty_struct *tty) wake_up_interruptible(&info->port.open_wait); } +static int esp_carrier_raised(struct tty_port *port) +{ + struct esp_struct *info = container_of(port, struct esp_struct, port); + serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT); + if (serial_in(info, UART_ESI_STAT2) & UART_MSR_DCD) + return 1; + return 0; +} + /* * ------------------------------------------------------------ * esp_open() and friends @@ -2066,17 +2075,19 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, int retval; int do_clocal = 0; unsigned long flags; + int cd; + struct tty_port *port = &info->port; /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ if (tty_hung_up_p(filp) || - (info->port.flags & ASYNC_CLOSING)) { - if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->port.close_wait); + (port->flags & ASYNC_CLOSING)) { + if (port->flags & ASYNC_CLOSING) + interruptible_sleep_on(&port->close_wait); #ifdef SERIAL_DO_RESTART - if (info->port.flags & ASYNC_HUP_NOTIFY) + if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -2091,7 +2102,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -2101,20 +2112,20 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->port.count is dropped by one, so that + * this loop, port->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->port.open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "block_til_ready before block: ttys%d, count = %d\n", - info->line, info->port.count); + info->line, port->count); #endif spin_lock_irqsave(&info->lock, flags); if (!tty_hung_up_p(filp)) - info->port.count--; - info->port.blocked_open++; + port->count--; + port->blocked_open++; while (1) { if ((tty->termios->c_cflag & CBAUD)) { unsigned int scratch; @@ -2129,9 +2140,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->port.flags & ASYNC_INITIALIZED)) { + !(port->flags & ASYNC_INITIALIZED)) { #ifdef SERIAL_DO_RESTART - if (info->port.flags & ASYNC_HUP_NOTIFY) + if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -2141,11 +2152,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, break; } - serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT); - if (serial_in(info, UART_ESI_STAT2) & UART_MSR_DCD) - do_clocal = 1; + cd = tty_port_carrier_raised(port); - if (!(info->port.flags & ASYNC_CLOSING) && + if (!(port->flags & ASYNC_CLOSING) && (do_clocal)) break; if (signal_pending(current)) { @@ -2154,25 +2163,25 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "block_til_ready blocking: ttys%d, count = %d\n", - info->line, info->port.count); + info->line, port->count); #endif spin_unlock_irqrestore(&info->lock, flags); schedule(); spin_lock_irqsave(&info->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->port.open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); if (!tty_hung_up_p(filp)) - info->port.count++; - info->port.blocked_open--; + port->count++; + port->blocked_open--; spin_unlock_irqrestore(&info->lock, flags); #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "block_til_ready after blocking: ttys%d, count = %d\n", - info->line, info->port.count); + info->line, port->count); #endif if (retval) return retval; - info->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -2329,6 +2338,10 @@ static const struct tty_operations esp_ops = { .tiocmset = esp_tiocmset, }; +static const struct tty_port_operations esp_port_ops = { + .esp_carrier_raised, +}; + /* * The serial driver boot-time initialization code! */ @@ -2415,6 +2428,8 @@ static int __init espserial_init(void) offset = 0; do { + tty_port_init(&info->port); + info->port.ops = &esp_port_ops; info->io_port = esp[i] + offset; info->irq = irq[i]; info->line = (i * 8) + (offset / 8); @@ -2437,8 +2452,6 @@ static int __init espserial_init(void) info->config.flow_off = flow_off; info->config.pio_threshold = pio_threshold; info->next_port = ports; - init_waitqueue_head(&info->port.open_wait); - init_waitqueue_head(&info->port.close_wait); init_waitqueue_head(&info->delta_msr_wait); init_waitqueue_head(&info->break_wait); ports = info; diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index c6090f84a2e4..2356994ee010 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -397,7 +397,8 @@ void gs_hangup(struct tty_struct *tty) int gs_block_til_ready(void *port_, struct file * filp) { - struct gs_port *port = port_; + struct gs_port *gp = port_; + struct tty_port *port = &gp->port; DECLARE_WAITQUEUE(wait, current); int retval; int do_clocal = 0; @@ -409,16 +410,16 @@ int gs_block_til_ready(void *port_, struct file * filp) if (!port) return 0; - tty = port->port.tty; + tty = port->tty; gs_dprintk (GS_DEBUG_BTR, "Entering gs_block_till_ready.\n"); /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ - if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) { - interruptible_sleep_on(&port->port.close_wait); - if (port->port.flags & ASYNC_HUP_NOTIFY) + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -432,7 +433,7 @@ int gs_block_til_ready(void *port_, struct file * filp) */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - port->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -444,34 +445,34 @@ int gs_block_til_ready(void *port_, struct file * filp) /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, port->port.count is dropped by one, so that + * this loop, port->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&port->port.open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n"); - spin_lock_irqsave(&port->driver_lock, flags); + spin_lock_irqsave(&gp->driver_lock, flags); if (!tty_hung_up_p(filp)) { - port->port.count--; + port->count--; } - spin_unlock_irqrestore(&port->driver_lock, flags); - port->port.blocked_open++; + spin_unlock_irqrestore(&gp->driver_lock, flags); + port->blocked_open++; while (1) { - CD = port->rd->get_CD (port); + CD = tty_port_carrier_raised(port); gs_dprintk (GS_DEBUG_BTR, "CD is now %d.\n", CD); set_current_state (TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(port->port.flags & ASYNC_INITIALIZED)) { - if (port->port.flags & ASYNC_HUP_NOTIFY) + !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } - if (!(port->port.flags & ASYNC_CLOSING) && + if (!(port->flags & ASYNC_CLOSING) && (do_clocal || CD)) break; gs_dprintk (GS_DEBUG_BTR, "signal_pending is now: %d (%lx)\n", @@ -483,17 +484,17 @@ int gs_block_til_ready(void *port_, struct file * filp) schedule(); } gs_dprintk (GS_DEBUG_BTR, "Got out of the loop. (%d)\n", - port->port.blocked_open); + port->blocked_open); set_current_state (TASK_RUNNING); - remove_wait_queue(&port->port.open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); if (!tty_hung_up_p(filp)) { - port->port.count++; + port->count++; } - port->port.blocked_open--; + port->blocked_open--; if (retval) return retval; - port->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; func_exit (); return 0; } diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 04e4549299ba..b3da4858fd4a 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -830,20 +830,28 @@ static int isicom_setup_port(struct tty_struct *tty) return 0; } +static int isicom_carrier_raised(struct tty_port *port) +{ + struct isi_port *ip = container_of(port, struct isi_port, port); + return (ip->status & ISI_DCD)?1 : 0; +} + static int block_til_ready(struct tty_struct *tty, struct file *filp, - struct isi_port *port) + struct isi_port *ip) { - struct isi_board *card = port->card; + struct isi_board *card = ip->card; + struct tty_port *port = &ip->port; int do_clocal = 0, retval; unsigned long flags; DECLARE_WAITQUEUE(wait, current); + int cd; /* block if port is in the process of being closed */ - if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) { + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { pr_dbg("block_til_ready: close in progress.\n"); - interruptible_sleep_on(&port->port.close_wait); - if (port->port.flags & ASYNC_HUP_NOTIFY) + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -854,7 +862,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { pr_dbg("block_til_ready: non-block mode.\n"); - port->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -864,29 +872,29 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, /* block waiting for DCD to be asserted, and while callout dev is busy */ retval = 0; - add_wait_queue(&port->port.open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); spin_lock_irqsave(&card->card_lock, flags); if (!tty_hung_up_p(filp)) - port->port.count--; - port->port.blocked_open++; + port->count--; + port->blocked_open++; spin_unlock_irqrestore(&card->card_lock, flags); while (1) { - raise_dtr_rts(port); + raise_dtr_rts(ip); set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(port->port.flags & ASYNC_INITIALIZED)) { - if (port->port.flags & ASYNC_HUP_NOTIFY) + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } - if (!(port->port.flags & ASYNC_CLOSING) && - (do_clocal || (port->status & ISI_DCD))) { + cd = tty_port_carrier_raised(port); + if (!(port->flags & ASYNC_CLOSING) && + (do_clocal || cd)) break; - } if (signal_pending(current)) { retval = -ERESTARTSYS; break; @@ -894,15 +902,15 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, schedule(); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->port.open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); spin_lock_irqsave(&card->card_lock, flags); if (!tty_hung_up_p(filp)) - port->port.count++; - port->port.blocked_open--; + port->count++; + port->blocked_open--; spin_unlock_irqrestore(&card->card_lock, flags); if (retval) return retval; - port->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -1452,6 +1460,10 @@ static const struct tty_operations isicom_ops = { .break_ctl = isicom_send_break, }; +static const struct tty_port_operations isicom_port_ops = { + .carrier_raised = isicom_carrier_raised, +}; + static int __devinit reset_card(struct pci_dev *pdev, const unsigned int card, unsigned int *signature) { @@ -1794,6 +1806,7 @@ static int __init isicom_init(void) spin_lock_init(&isi_card[idx].card_lock); for (channel = 0; channel < 16; channel++, port++) { tty_port_init(&port->port); + port->port.ops = &isicom_port_ops; port->magic = ISICOM_MAGIC; port->card = &isi_card[idx]; port->channel = channel; diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 4b10770fa937..c4682f9e34bb 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -151,7 +151,7 @@ static char *stli_drvversion = "5.6.0"; static char *stli_serialname = "ttyE"; static struct tty_driver *stli_serial; - +static const struct tty_port_operations stli_port_ops; #define STLI_TXBUFSIZE 4096 @@ -1183,6 +1183,12 @@ static int stli_setport(struct tty_struct *tty) /*****************************************************************************/ +static int stli_carrier_raised(struct tty_port *port) +{ + struct stliport *portp = container_of(port, struct stliport, port); + return (portp->sigs & TIOCM_CD) ? 1 : 0; +} + /* * Possibly need to wait for carrier (DCD signal) to come high. Say * maybe because if we are clocal then we don't need to wait... @@ -1193,6 +1199,7 @@ static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp, { unsigned long flags; int rc, doclocal; + struct tty_port *port = &portp->port; rc = 0; doclocal = 0; @@ -1203,7 +1210,7 @@ static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp, spin_lock_irqsave(&stli_lock, flags); portp->openwaitcnt++; if (! tty_hung_up_p(filp)) - portp->port.count--; + port->count--; spin_unlock_irqrestore(&stli_lock, flags); for (;;) { @@ -1212,27 +1219,27 @@ static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp, &portp->asig, sizeof(asysigs_t), 0)) < 0) break; if (tty_hung_up_p(filp) || - ((portp->port.flags & ASYNC_INITIALIZED) == 0)) { - if (portp->port.flags & ASYNC_HUP_NOTIFY) + ((port->flags & ASYNC_INITIALIZED) == 0)) { + if (port->flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else rc = -ERESTARTSYS; break; } - if (((portp->port.flags & ASYNC_CLOSING) == 0) && - (doclocal || (portp->sigs & TIOCM_CD))) { + if (((port->flags & ASYNC_CLOSING) == 0) && + (doclocal || tty_port_carrier_raised(port))) { break; } if (signal_pending(current)) { rc = -ERESTARTSYS; break; } - interruptible_sleep_on(&portp->port.open_wait); + interruptible_sleep_on(&port->open_wait); } spin_lock_irqsave(&stli_lock, flags); if (! tty_hung_up_p(filp)) - portp->port.count++; + port->count++; portp->openwaitcnt--; spin_unlock_irqrestore(&stli_lock, flags); @@ -2696,6 +2703,7 @@ static int stli_initports(struct stlibrd *brdp) continue; } tty_port_init(&portp->port); + portp->port.ops = &stli_port_ops; portp->magic = STLI_PORTMAGIC; portp->portnr = i; portp->brdnr = brdp->brdnr; @@ -4518,6 +4526,10 @@ static const struct tty_operations stli_ops = { .tiocmset = stli_tiocmset, }; +static const struct tty_port_operations stli_port_ops = { + .carrier_raised = stli_carrier_raised, +}; + /*****************************************************************************/ /* * Loadable module initialization stuff. diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 12d327a2c9ba..8b0da97d5293 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -206,6 +206,7 @@ static void moxa_poll(unsigned long); static void moxa_set_tty_param(struct tty_struct *, struct ktermios *); static void moxa_setup_empty_event(struct tty_struct *); static void moxa_shut_down(struct tty_struct *); +static int moxa_carrier_raised(struct tty_port *); /* * moxa board interface functions: */ @@ -405,6 +406,10 @@ static const struct tty_operations moxa_ops = { .tiocmset = moxa_tiocmset, }; +static const struct tty_port_operations moxa_port_ops = { + .carrier_raised = moxa_carrier_raised, +}; + static struct tty_driver *moxaDriver; static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0); static DEFINE_SPINLOCK(moxa_lock); @@ -826,6 +831,7 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) for (i = 0, p = brd->ports; i < MAX_PORTS_PER_BOARD; i++, p++) { tty_port_init(&p->port); + p->port.ops = &moxa_port_ops; p->type = PORT_16550A; p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; } @@ -1115,15 +1121,27 @@ static void moxa_close_port(struct tty_struct *tty) tty_port_tty_set(&ch->port, NULL); } +static int moxa_carrier_raised(struct tty_port *port) +{ + struct moxa_port *ch = container_of(port, struct moxa_port, port); + int dcd; + + spin_lock_bh(&moxa_lock); + dcd = ch->DCDState; + spin_unlock_bh(&moxa_lock); + return dcd; +} + static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp, struct moxa_port *ch) { + struct tty_port *port = &ch->port; DEFINE_WAIT(wait); int retval = 0; u8 dcd; while (1) { - prepare_to_wait(&ch->port.open_wait, &wait, TASK_INTERRUPTIBLE); + prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp)) { #ifdef SERIAL_DO_RESTART retval = -ERESTARTSYS; @@ -1132,9 +1150,7 @@ static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp, #endif break; } - spin_lock_bh(&moxa_lock); - dcd = ch->DCDState; - spin_unlock_bh(&moxa_lock); + dcd = tty_port_carrier_raised(port); if (dcd) break; @@ -1144,7 +1160,7 @@ static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp, } schedule(); } - finish_wait(&ch->port.open_wait, &wait); + finish_wait(&port->open_wait, &wait); return retval; } diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 047766915411..eafbbcf355e7 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -541,13 +541,21 @@ static unsigned char mxser_get_msr(int baseaddr, int mode, int port) return status; } +static int mxser_carrier_raised(struct tty_port *port) +{ + struct mxser_port *mp = container_of(port, struct mxser_port, port); + return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0; +} + static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, - struct mxser_port *port) + struct mxser_port *mp) { DECLARE_WAITQUEUE(wait, current); int retval; int do_clocal = 0; unsigned long flags; + int cd; + struct tty_port *port = &mp->port; /* * If non-blocking mode is set, or the port is not enabled, @@ -555,7 +563,7 @@ static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, */ if ((filp->f_flags & O_NONBLOCK) || test_bit(TTY_IO_ERROR, &tty->flags)) { - port->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -565,34 +573,33 @@ static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, port->port.count is dropped by one, so that + * this loop, port->count is dropped by one, so that * mxser_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&port->port.open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); - spin_lock_irqsave(&port->slock, flags); + spin_lock_irqsave(&mp->slock, flags); if (!tty_hung_up_p(filp)) - port->port.count--; - spin_unlock_irqrestore(&port->slock, flags); - port->port.blocked_open++; + port->count--; + spin_unlock_irqrestore(&mp->slock, flags); + port->blocked_open++; while (1) { - spin_lock_irqsave(&port->slock, flags); - outb(inb(port->ioaddr + UART_MCR) | - UART_MCR_DTR | UART_MCR_RTS, port->ioaddr + UART_MCR); - spin_unlock_irqrestore(&port->slock, flags); + spin_lock_irqsave(&mp->slock, flags); + outb(inb(mp->ioaddr + UART_MCR) | + UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR); + spin_unlock_irqrestore(&mp->slock, flags); set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(port->port.flags & ASYNC_INITIALIZED)) { - if (port->port.flags & ASYNC_HUP_NOTIFY) + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } - if (!(port->port.flags & ASYNC_CLOSING) && - (do_clocal || - (inb(port->ioaddr + UART_MSR) & UART_MSR_DCD))) + cd = tty_port_carrier_raised(port); + if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd)) break; if (signal_pending(current)) { retval = -ERESTARTSYS; @@ -601,13 +608,13 @@ static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, schedule(); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->port.open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); if (!tty_hung_up_p(filp)) - port->port.count++; - port->port.blocked_open--; + port->count++; + port->blocked_open--; if (retval) return retval; - port->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -2449,6 +2456,10 @@ static const struct tty_operations mxser_ops = { .tiocmset = mxser_tiocmset, }; +struct tty_port_operations mxser_port_ops = { + .carrier_raised = mxser_carrier_raised, +}; + /* * The MOXA Smartio/Industio serial driver boot-time initialization code! */ @@ -2482,6 +2493,7 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, for (i = 0; i < brd->info->nports; i++) { info = &brd->ports[i]; tty_port_init(&info->port); + info->port.ops = &mxser_port_ops; info->board = brd; info->stop_rx = 0; info->ldisc_stop_rx = 0; diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c index a8f68a3f14dd..ec2afd139472 100644 --- a/drivers/char/rio/rio_linux.c +++ b/drivers/char/rio/rio_linux.c @@ -173,7 +173,7 @@ static void rio_disable_tx_interrupts(void *ptr); static void rio_enable_tx_interrupts(void *ptr); static void rio_disable_rx_interrupts(void *ptr); static void rio_enable_rx_interrupts(void *ptr); -static int rio_get_CD(void *ptr); +static int rio_carrier_raised(struct tty_port *port); static void rio_shutdown_port(void *ptr); static int rio_set_real_termios(void *ptr); static void rio_hungup(void *ptr); @@ -224,7 +224,6 @@ static struct real_driver rio_real_driver = { rio_enable_tx_interrupts, rio_disable_rx_interrupts, rio_enable_rx_interrupts, - rio_get_CD, rio_shutdown_port, rio_set_real_termios, rio_chars_in_buffer, @@ -476,9 +475,9 @@ static void rio_enable_rx_interrupts(void *ptr) /* Jeez. Isn't this simple? */ -static int rio_get_CD(void *ptr) +static int rio_carrier_raised(struct tty_port *port) { - struct Port *PortP = ptr; + struct Port *PortP = container_of(port, struct Port, gs.port); int rv; func_enter(); @@ -806,7 +805,9 @@ static void *ckmalloc(int size) return p; } - +static const struct tty_port_operations rio_port_ops = { + .carrier_raised = rio_carrier_raised, +}; static int rio_init_datastructures(void) { @@ -842,17 +843,14 @@ static int rio_init_datastructures(void) goto free6; } rio_dprintk(RIO_DEBUG_INIT, "initing port %d (%d)\n", i, port->Mapped); + tty_port_init(&port->gs.port); + port->gs.port.ops = &rio_port_ops; port->PortNum = i; port->gs.magic = RIO_MAGIC; port->gs.close_delay = HZ / 2; port->gs.closing_wait = 30 * HZ; port->gs.rd = &rio_real_driver; spin_lock_init(&port->portSem); - /* - * Initializing wait queue - */ - init_waitqueue_head(&port->gs.port.open_wait); - init_waitqueue_head(&port->gs.port.close_wait); } #else /* We could postpone initializing them to when they are configured. */ diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 2c6c8f33d6b4..6ad1c2aa2a98 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -857,23 +857,40 @@ static void rc_shutdown_port(struct tty_struct *tty, rc_shutdown_board(bp); } +static int carrier_raised(struct tty_port *port) +{ + struct riscom_port *p = container_of(port, struct riscom_port, port); + struct riscom_board *bp = port_Board(p); + unsigned long flags; + int CD; + + spin_lock_irqsave(&riscom_lock, flags); + rc_out(bp, CD180_CAR, port_No(p)); + CD = rc_in(bp, CD180_MSVR) & MSVR_CD; + rc_out(bp, CD180_MSVR, MSVR_RTS); + bp->DTR &= ~(1u << port_No(p)); + rc_out(bp, RC_DTR, bp->DTR); + spin_unlock_irqrestore(&riscom_lock, flags); + return CD; +} + static int block_til_ready(struct tty_struct *tty, struct file *filp, - struct riscom_port *port) + struct riscom_port *rp) { DECLARE_WAITQUEUE(wait, current); - struct riscom_board *bp = port_Board(port); int retval; int do_clocal = 0; int CD; unsigned long flags; + struct tty_port *port = &rp->port; /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ - if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) { - interruptible_sleep_on(&port->port.close_wait); - if (port->port.flags & ASYNC_HUP_NOTIFY) + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -885,7 +902,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - port->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -900,37 +917,29 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&port->port.open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); spin_lock_irqsave(&riscom_lock, flags); if (!tty_hung_up_p(filp)) - port->port.count--; + port->count--; spin_unlock_irqrestore(&riscom_lock, flags); - port->port.blocked_open++; + port->blocked_open++; while (1) { - spin_lock_irqsave(&riscom_lock, flags); - - rc_out(bp, CD180_CAR, port_No(port)); - CD = rc_in(bp, CD180_MSVR) & MSVR_CD; - rc_out(bp, CD180_MSVR, MSVR_RTS); - bp->DTR &= ~(1u << port_No(port)); - rc_out(bp, RC_DTR, bp->DTR); - - spin_unlock_irqrestore(&riscom_lock, flags); + CD = tty_port_carrier_raised(port); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(port->port.flags & ASYNC_INITIALIZED)) { - if (port->port.flags & ASYNC_HUP_NOTIFY) + !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } - if (!(port->port.flags & ASYNC_CLOSING) && + if (!(port->flags & ASYNC_CLOSING) && (do_clocal || CD)) break; if (signal_pending(current)) { @@ -940,14 +949,14 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, schedule(); } __set_current_state(TASK_RUNNING); - remove_wait_queue(&port->port.open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); if (!tty_hung_up_p(filp)) - port->port.count++; - port->port.blocked_open--; + port->count++; + port->blocked_open--; if (retval) return retval; - port->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -1510,6 +1519,11 @@ static const struct tty_operations riscom_ops = { .break_ctl = rc_send_break, }; +static const struct tty_port_operations riscom_port_ops = { + .carrier_raised = carrier_raised, +}; + + static int __init rc_init_drivers(void) { int error; @@ -1541,6 +1555,7 @@ static int __init rc_init_drivers(void) memset(rc_port, 0, sizeof(rc_port)); for (i = 0; i < RC_NPORT * RC_NBOARD; i++) { tty_port_init(&rc_port[i].port); + rc_port[i].port.ops = &riscom_port_ops; rc_port[i].magic = RISCOM8_MAGIC; } return 0; diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 584d791e84a6..4a4110e703a5 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -135,6 +135,7 @@ static int rcktpt_type[NUM_BOARDS]; static int is_PCI[NUM_BOARDS]; static rocketModel_t rocketModel[NUM_BOARDS]; static int max_board; +static const struct tty_port_operations rocket_port_ops; /* * The following arrays define the interrupt bits corresponding to each AIOP. @@ -649,9 +650,8 @@ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) info->board = board; info->aiop = aiop; info->chan = chan; - info->port.closing_wait = 3000; - info->port.close_delay = 50; - init_waitqueue_head(&info->port.open_wait); + tty_port_init(&info->port); + info->port.ops = &rocket_port_ops; init_completion(&info->close_wait); info->flags &= ~ROCKET_MODE_MASK; switch (pc104[board][line]) { @@ -864,11 +864,18 @@ static void configure_r_port(struct r_port *info, } } +static int carrier_raised(struct tty_port *port) +{ + struct r_port *info = container_of(port, struct r_port, port); + return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0; +} + /* info->port.count is considered critical, protected by spinlocks. */ static int block_til_ready(struct tty_struct *tty, struct file *filp, struct r_port *info) { DECLARE_WAITQUEUE(wait, current); + struct tty_port *port = &info->port; int retval; int do_clocal = 0, extra_count = 0; unsigned long flags; @@ -898,13 +905,13 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, /* * Block waiting for the carrier detect and the line to become free. While we are in - * this loop, info->port.count is dropped by one, so that rp_close() knows when to free things. + * this loop, port->count is dropped by one, so that rp_close() knows when to free things. * We restore it upon exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->port.open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); #ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "block_til_ready before block: ttyR%d, count = %d\n", info->line, info->port.count); + printk(KERN_INFO "block_til_ready before block: ttyR%d, count = %d\n", info->line, port->count); #endif spin_lock_irqsave(&info->slock, flags); @@ -913,10 +920,10 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, #else if (!tty_hung_up_p(filp)) { extra_count = 1; - info->port.count--; + port->count--; } #endif - info->port.blocked_open++; + port->blocked_open++; spin_unlock_irqrestore(&info->slock, flags); @@ -933,7 +940,8 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, retval = -ERESTARTSYS; break; } - if (!(info->flags & ROCKET_CLOSING) && (do_clocal || (sGetChanStatusLo(&info->channel) & CD_ACT))) + if (!(info->flags & ROCKET_CLOSING) && + (do_clocal || tty_port_carrier_raised(port))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; @@ -941,24 +949,24 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } #ifdef ROCKET_DEBUG_OPEN printk(KERN_INFO "block_til_ready blocking: ttyR%d, count = %d, flags=0x%0x\n", - info->line, info->port.count, info->flags); + info->line, port->count, info->flags); #endif schedule(); /* Don't hold spinlock here, will hang PC */ } __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->port.open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); spin_lock_irqsave(&info->slock, flags); if (extra_count) - info->port.count++; - info->port.blocked_open--; + port->count++; + port->blocked_open--; spin_unlock_irqrestore(&info->slock, flags); #ifdef ROCKET_DEBUG_OPEN printk(KERN_INFO "block_til_ready after blocking: ttyR%d, count = %d\n", - info->line, info->port.count); + info->line, port->count); #endif if (retval) return retval; @@ -2371,6 +2379,10 @@ static const struct tty_operations rocket_ops = { .tiocmset = rp_tiocmset, }; +static const struct tty_port_operations rocket_port_ops = { + .carrier_raised = carrier_raised, +}; + /* * The module "startup" routine; it's run when the module is loaded. */ diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c index 7b0c35207d9b..0c97f34df63a 100644 --- a/drivers/char/ser_a2232.c +++ b/drivers/char/ser_a2232.c @@ -122,7 +122,7 @@ static void a2232_disable_tx_interrupts(void *ptr); static void a2232_enable_tx_interrupts(void *ptr); static void a2232_disable_rx_interrupts(void *ptr); static void a2232_enable_rx_interrupts(void *ptr); -static int a2232_get_CD(void *ptr); +static int a2232_carrier_raised(struct tty_port *port); static void a2232_shutdown_port(void *ptr); static int a2232_set_real_termios(void *ptr); static int a2232_chars_in_buffer(void *ptr); @@ -148,7 +148,6 @@ static struct real_driver a2232_real_driver = { a2232_enable_tx_interrupts, a2232_disable_rx_interrupts, a2232_enable_rx_interrupts, - a2232_get_CD, a2232_shutdown_port, a2232_set_real_termios, a2232_chars_in_buffer, @@ -260,9 +259,10 @@ static void a2232_enable_rx_interrupts(void *ptr) port->disable_rx = 0; } -static int a2232_get_CD(void *ptr) +static int a2232_carrier_raised(struct tty_port *port) { - return ((struct a2232_port *) ptr)->cd_status; + struct a2232_port *ap = container_of(port, struct a2232_port, gs.port); + return ap->cd_status; } static void a2232_shutdown_port(void *ptr) @@ -638,6 +638,10 @@ int ch, err, n, p; return IRQ_HANDLED; } +static const struct tty_port_operations a2232_port_ops = { + .carrier_raised = a2232_carrier_raised, +}; + static void a2232_init_portstructs(void) { struct a2232_port *port; @@ -645,6 +649,8 @@ static void a2232_init_portstructs(void) for (i = 0; i < MAX_A2232_BOARDS*NUMLINES; i++) { port = a2232_ports + i; + tty_port_init(&port->gs.port); + port->gs.port.ops = &a2232_port_ops; port->which_a2232 = i/NUMLINES; port->which_port_on_a2232 = i%NUMLINES; port->disable_rx = port->throttle_input = port->cd_status = 0; @@ -652,11 +658,6 @@ static void a2232_init_portstructs(void) port->gs.close_delay = HZ/2; port->gs.closing_wait = 30 * HZ; port->gs.rd = &a2232_real_driver; -#ifdef NEW_WRITE_LOCKING - mutex_init(&(port->gs.port_write_mutex)); -#endif - init_waitqueue_head(&port->gs.port.open_wait); - init_waitqueue_head(&port->gs.port.close_wait); } } diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 963b03fb29e5..12aecdaf61ec 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -130,6 +130,8 @@ static char stl_unwanted[SC26198_RXFIFOSIZE]; static DEFINE_MUTEX(stl_brdslock); static struct stlbrd *stl_brds[STL_MAXBRDS]; +static const struct tty_port_operations stl_port_ops; + /* * Per board state flags. Used with the state field of the board struct. * Not really much here! @@ -786,6 +788,12 @@ static int stl_open(struct tty_struct *tty, struct file *filp) /*****************************************************************************/ +static int stl_carrier_raised(struct tty_port *port) +{ + struct stlport *portp = container_of(port, struct stlport, port); + return (portp->sigs & TIOCM_CD) ? 1 : 0; +} + /* * Possibly need to wait for carrier (DCD signal) to come high. Say * maybe because if we are clocal then we don't need to wait... @@ -796,6 +804,7 @@ static int stl_waitcarrier(struct tty_struct *tty, struct stlport *portp, { unsigned long flags; int rc, doclocal; + struct tty_port *port = &portp->port; pr_debug("stl_waitcarrier(portp=%p,filp=%p)\n", portp, filp); @@ -809,32 +818,32 @@ static int stl_waitcarrier(struct tty_struct *tty, struct stlport *portp, portp->openwaitcnt++; if (! tty_hung_up_p(filp)) - portp->port.count--; + port->count--; for (;;) { /* Takes brd_lock internally */ stl_setsignals(portp, 1, 1); if (tty_hung_up_p(filp) || - ((portp->port.flags & ASYNC_INITIALIZED) == 0)) { - if (portp->port.flags & ASYNC_HUP_NOTIFY) + ((port->flags & ASYNC_INITIALIZED) == 0)) { + if (port->flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else rc = -ERESTARTSYS; break; } - if (((portp->port.flags & ASYNC_CLOSING) == 0) && - (doclocal || (portp->sigs & TIOCM_CD))) + if (((port->flags & ASYNC_CLOSING) == 0) && + (doclocal || tty_port_carrier_raised(port))) break; if (signal_pending(current)) { rc = -ERESTARTSYS; break; } /* FIXME */ - interruptible_sleep_on(&portp->port.open_wait); + interruptible_sleep_on(&port->open_wait); } if (! tty_hung_up_p(filp)) - portp->port.count++; + port->count++; portp->openwaitcnt--; spin_unlock_irqrestore(&stallion_lock, flags); @@ -1776,6 +1785,7 @@ static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp) break; } tty_port_init(&portp->port); + portp->port.ops = &stl_port_ops; portp->magic = STL_PORTMAGIC; portp->portnr = i; portp->brdnr = panelp->brdnr; @@ -2659,6 +2669,10 @@ static const struct tty_operations stl_ops = { .tiocmset = stl_tiocmset, }; +static const struct tty_port_operations stl_port_ops = { + .carrier_raised = stl_carrier_raised, +}; + /*****************************************************************************/ /* CD1400 HARDWARE FUNCTIONS */ /*****************************************************************************/ diff --git a/drivers/char/sx.c b/drivers/char/sx.c index ba4e86281fbf..a71bc58abe7f 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -279,7 +279,7 @@ static void sx_disable_tx_interrupts(void *ptr); static void sx_enable_tx_interrupts(void *ptr); static void sx_disable_rx_interrupts(void *ptr); static void sx_enable_rx_interrupts(void *ptr); -static int sx_get_CD(void *ptr); +static int sx_carrier_raised(struct tty_port *port); static void sx_shutdown_port(void *ptr); static int sx_set_real_termios(void *ptr); static void sx_close(void *ptr); @@ -360,7 +360,6 @@ static struct real_driver sx_real_driver = { sx_enable_tx_interrupts, sx_disable_rx_interrupts, sx_enable_rx_interrupts, - sx_get_CD, sx_shutdown_port, sx_set_real_termios, sx_chars_in_buffer, @@ -791,7 +790,7 @@ static int sx_getsignals(struct sx_port *port) sx_dprintk(SX_DEBUG_MODEMSIGNALS, "getsignals: %d/%d (%d/%d) " "%02x/%02x\n", (o_stat & OP_DTR) != 0, (o_stat & OP_RTS) != 0, - port->c_dcd, sx_get_CD(port), + port->c_dcd, tty_port_carrier_raised(&port->gs.port), sx_read_channel_byte(port, hi_ip), sx_read_channel_byte(port, hi_state)); @@ -1190,7 +1189,7 @@ static inline void sx_check_modem_signals(struct sx_port *port) hi_state = sx_read_channel_byte(port, hi_state); sx_dprintk(SX_DEBUG_MODEMSIGNALS, "Checking modem signals (%d/%d)\n", - port->c_dcd, sx_get_CD(port)); + port->c_dcd, tty_port_carrier_raised(&port->gs.port)); if (hi_state & ST_BREAK) { hi_state &= ~ST_BREAK; @@ -1202,11 +1201,11 @@ static inline void sx_check_modem_signals(struct sx_port *port) hi_state &= ~ST_DCD; sx_dprintk(SX_DEBUG_MODEMSIGNALS, "got a DCD change.\n"); sx_write_channel_byte(port, hi_state, hi_state); - c_dcd = sx_get_CD(port); + c_dcd = tty_port_carrier_raised(&port->gs.port); sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD is now %d\n", c_dcd); if (c_dcd != port->c_dcd) { port->c_dcd = c_dcd; - if (sx_get_CD(port)) { + if (tty_port_carrier_raised(&port->gs.port)) { /* DCD went UP */ if ((sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED) && @@ -1415,13 +1414,10 @@ static void sx_enable_rx_interrupts(void *ptr) } /* Jeez. Isn't this simple? */ -static int sx_get_CD(void *ptr) +static int sx_carrier_raised(struct tty_port *port) { - struct sx_port *port = ptr; - func_enter2(); - - func_exit(); - return ((sx_read_channel_byte(port, hi_ip) & IP_DCD) != 0); + struct sx_port *sp = container_of(port, struct sx_port, gs.port); + return ((sx_read_channel_byte(sp, hi_ip) & IP_DCD) != 0); } /* Jeez. Isn't this simple? */ @@ -1536,7 +1532,7 @@ static int sx_open(struct tty_struct *tty, struct file *filp) } /* tty->low_latency = 1; */ - port->c_dcd = sx_get_CD(port); + port->c_dcd = sx_carrier_raised(&port->gs.port); sx_dprintk(SX_DEBUG_OPEN, "at open: cd=%d\n", port->c_dcd); func_exit(); @@ -2354,6 +2350,10 @@ static const struct tty_operations sx_ops = { .tiocmset = sx_tiocmset, }; +static const struct tty_port_operations sx_port_ops = { + .carrier_raised = sx_carrier_raised, +}; + static int sx_init_drivers(void) { int error; @@ -2410,6 +2410,7 @@ static int sx_init_portstructs(int nboards, int nports) for (j = 0; j < boards[i].nports; j++) { sx_dprintk(SX_DEBUG_INIT, "initing port %d\n", j); tty_port_init(&port->gs.port); + port->gs.port.ops = &sx_port_ops; port->gs.magic = SX_MAGIC; port->gs.close_delay = HZ / 2; port->gs.closing_wait = 30 * HZ; diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 500f5176b6ba..fb2e6b5e0ef1 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -3281,6 +3281,23 @@ static void mgsl_hangup(struct tty_struct *tty) } /* end of mgsl_hangup() */ +/* + * carrier_raised() + * + * Return true if carrier is raised + */ + +static int carrier_raised(struct tty_port *port) +{ + unsigned long flags; + struct mgsl_struct *info = container_of(port, struct mgsl_struct, port); + + spin_lock_irqsave(&info->irq_spinlock, flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock, flags); + return (info->serial_signals & SerialSignal_DCD) ? 1 : 0; +} + /* block_til_ready() * * Block the current process until the specified port @@ -3302,6 +3319,8 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, bool do_clocal = false; bool extra_count = false; unsigned long flags; + int dcd; + struct tty_port *port = &info->port; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):block_til_ready on %s\n", @@ -3309,7 +3328,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ /* nonblock mode is set or port is not enabled */ - info->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -3318,25 +3337,25 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, /* Wait for carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->port.count is dropped by one, so that + * this loop, port->count is dropped by one, so that * mgsl_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->port.open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):block_til_ready before block on %s count=%d\n", - __FILE__,__LINE__, tty->driver->name, info->port.count ); + __FILE__,__LINE__, tty->driver->name, port->count ); spin_lock_irqsave(&info->irq_spinlock, flags); if (!tty_hung_up_p(filp)) { extra_count = true; - info->port.count--; + port->count--; } spin_unlock_irqrestore(&info->irq_spinlock, flags); - info->port.blocked_open++; + port->blocked_open++; while (1) { if (tty->termios->c_cflag & CBAUD) { @@ -3348,20 +3367,16 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(info->port.flags & ASYNC_INITIALIZED)){ - retval = (info->port.flags & ASYNC_HUP_NOTIFY) ? + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ + retval = (port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; break; } - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_get_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); + dcd = tty_port_carrier_raised(&info->port); - if (!(info->port.flags & ASYNC_CLOSING) && - (do_clocal || (info->serial_signals & SerialSignal_DCD)) ) { + if (!(port->flags & ASYNC_CLOSING) && (do_clocal || dcd)) break; - } if (signal_pending(current)) { retval = -ERESTARTSYS; @@ -3370,24 +3385,24 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):block_til_ready blocking on %s count=%d\n", - __FILE__,__LINE__, tty->driver->name, info->port.count ); + __FILE__,__LINE__, tty->driver->name, port->count ); schedule(); } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->port.open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); if (extra_count) - info->port.count++; - info->port.blocked_open--; + port->count++; + port->blocked_open--; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):block_til_ready after blocking on %s count=%d\n", - __FILE__,__LINE__, tty->driver->name, info->port.count ); + __FILE__,__LINE__, tty->driver->name, port->count ); if (!retval) - info->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return retval; @@ -4304,6 +4319,11 @@ static void mgsl_add_device( struct mgsl_struct *info ) } /* end of mgsl_add_device() */ +static const struct tty_port_operations mgsl_port_ops = { + .carrier_raised = carrier_raised, +}; + + /* mgsl_allocate_device() * * Allocate and initialize a device instance structure @@ -4322,6 +4342,7 @@ static struct mgsl_struct* mgsl_allocate_device(void) printk("Error can't allocate device instance data\n"); } else { tty_port_init(&info->port); + info->port.ops = &mgsl_port_ops; info->magic = MGSL_MAGIC; INIT_WORK(&info->task, mgsl_bh_handler); info->max_frame_size = 4096; diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 08911ed66494..39ccaba8ca37 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -3132,6 +3132,17 @@ static int tiocmset(struct tty_struct *tty, struct file *file, return 0; } +static int carrier_raised(struct tty_port *port) +{ + unsigned long flags; + struct slgt_info *info = container_of(port, struct slgt_info, port); + + spin_lock_irqsave(&info->lock,flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + return (info->signals & SerialSignal_DCD) ? 1 : 0; +} + /* * block current process until the device is ready to open */ @@ -3143,12 +3154,14 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, bool do_clocal = false; bool extra_count = false; unsigned long flags; + int cd; + struct tty_port *port = &info->port; DBGINFO(("%s block_til_ready\n", tty->driver->name)); if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ /* nonblock mode is set or port is not enabled */ - info->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -3157,21 +3170,21 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, /* Wait for carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->port.count is dropped by one, so that + * this loop, port->count is dropped by one, so that * close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->port.open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); spin_lock_irqsave(&info->lock, flags); if (!tty_hung_up_p(filp)) { extra_count = true; - info->port.count--; + port->count--; } spin_unlock_irqrestore(&info->lock, flags); - info->port.blocked_open++; + port->blocked_open++; while (1) { if ((tty->termios->c_cflag & CBAUD)) { @@ -3183,20 +3196,16 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(info->port.flags & ASYNC_INITIALIZED)){ - retval = (info->port.flags & ASYNC_HUP_NOTIFY) ? + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ + retval = (port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; break; } - spin_lock_irqsave(&info->lock,flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock,flags); + cd = tty_port_carrier_raised(port); - if (!(info->port.flags & ASYNC_CLOSING) && - (do_clocal || (info->signals & SerialSignal_DCD)) ) { + if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd )) break; - } if (signal_pending(current)) { retval = -ERESTARTSYS; @@ -3208,14 +3217,14 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->port.open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); if (extra_count) - info->port.count++; - info->port.blocked_open--; + port->count++; + port->blocked_open--; if (!retval) - info->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval)); return retval; @@ -3444,6 +3453,10 @@ static void add_device(struct slgt_info *info) #endif } +static const struct tty_port_operations slgt_port_ops = { + .carrier_raised = carrier_raised, +}; + /* * allocate device instance structure, return NULL on failure */ @@ -3458,6 +3471,7 @@ static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev driver_name, adapter_num, port_num)); } else { tty_port_init(&info->port); + info->port.ops = &slgt_port_ops; info->magic = MGSL_MAGIC; INIT_WORK(&info->task, bh_handler); info->max_frame_size = 4096; diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 6bdb44f7bec2..fcf1ec77450d 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -558,6 +558,7 @@ static void release_resources(SLMP_INFO *info); static int startup(SLMP_INFO *info); static int block_til_ready(struct tty_struct *tty, struct file * filp,SLMP_INFO *info); +static int carrier_raised(struct tty_port *port); static void shutdown(SLMP_INFO *info); static void program_hw(SLMP_INFO *info); static void change_params(SLMP_INFO *info); @@ -3318,7 +3319,17 @@ static int tiocmset(struct tty_struct *tty, struct file *file, return 0; } +static int carrier_raised(struct tty_port *port) +{ + SLMP_INFO *info = container_of(port, SLMP_INFO, port); + unsigned long flags; + spin_lock_irqsave(&info->lock,flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + + return (info->serial_signals & SerialSignal_DCD) ? 1 : 0; +} /* Block the current process until the specified port is ready to open. */ @@ -3330,6 +3341,8 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, bool do_clocal = false; bool extra_count = false; unsigned long flags; + int cd; + struct tty_port *port = &info->port; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s block_til_ready()\n", @@ -3338,7 +3351,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ /* nonblock mode is set or port is not enabled */ /* just verify that callout device is not active */ - info->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -3347,25 +3360,25 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, /* Wait for carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->port.count is dropped by one, so that + * this loop, port->count is dropped by one, so that * close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->port.open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s block_til_ready() before block, count=%d\n", - __FILE__,__LINE__, tty->driver->name, info->port.count ); + __FILE__,__LINE__, tty->driver->name, port->count ); spin_lock_irqsave(&info->lock, flags); if (!tty_hung_up_p(filp)) { extra_count = true; - info->port.count--; + port->count--; } spin_unlock_irqrestore(&info->lock, flags); - info->port.blocked_open++; + port->blocked_open++; while (1) { if ((tty->termios->c_cflag & CBAUD)) { @@ -3377,20 +3390,16 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(info->port.flags & ASYNC_INITIALIZED)){ - retval = (info->port.flags & ASYNC_HUP_NOTIFY) ? + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ + retval = (port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; break; } - spin_lock_irqsave(&info->lock,flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock,flags); + cd = tty_port_carrier_raised(port); - if (!(info->port.flags & ASYNC_CLOSING) && - (do_clocal || (info->serial_signals & SerialSignal_DCD)) ) { + if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd)) break; - } if (signal_pending(current)) { retval = -ERESTARTSYS; @@ -3399,24 +3408,24 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s block_til_ready() count=%d\n", - __FILE__,__LINE__, tty->driver->name, info->port.count ); + __FILE__,__LINE__, tty->driver->name, port->count ); schedule(); } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->port.open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); if (extra_count) - info->port.count++; - info->port.blocked_open--; + port->count++; + port->blocked_open--; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s block_til_ready() after, count=%d\n", - __FILE__,__LINE__, tty->driver->name, info->port.count ); + __FILE__,__LINE__, tty->driver->name, port->count ); if (!retval) - info->port.flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return retval; } @@ -3782,6 +3791,10 @@ static void add_device(SLMP_INFO *info) #endif } +static const struct tty_port_operations port_ops = { + .carrier_raised = carrier_raised, +}; + /* Allocate and initialize a device instance structure * * Return Value: pointer to SLMP_INFO if success, otherwise NULL @@ -3798,6 +3811,7 @@ static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) __FILE__,__LINE__, adapter_num, port_num); } else { tty_port_init(&info->port); + info->port.ops = &port_ops; info->magic = MGSL_MAGIC; INIT_WORK(&info->task, bh_handler); info->max_frame_size = 4096; @@ -3940,6 +3954,7 @@ static const struct tty_operations ops = { .tiocmset = tiocmset, }; + static void synclinkmp_cleanup(void) { int rc; diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index c8f8024cb40e..f54e40cbf023 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -94,3 +94,20 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) spin_unlock_irqrestore(&port->lock, flags); } EXPORT_SYMBOL(tty_port_tty_set); + +/** + * tty_port_carrier_raised - carrier raised check + * @port: tty port + * + * Wrapper for the carrier detect logic. For the moment this is used + * to hide some internal details. This will eventually become entirely + * internal to the tty port. + */ + +int tty_port_carrier_raised(struct tty_port *port) +{ + if (port->ops->carrier_raised == NULL) + return 1; + return port->ops->carrier_raised(port); +} +EXPORT_SYMBOL(tty_port_carrier_raised); diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c index 1718b3c481db..d4e1534c0e03 100644 --- a/drivers/char/vme_scc.c +++ b/drivers/char/vme_scc.c @@ -69,7 +69,7 @@ static void scc_disable_tx_interrupts(void * ptr); static void scc_enable_tx_interrupts(void * ptr); static void scc_disable_rx_interrupts(void * ptr); static void scc_enable_rx_interrupts(void * ptr); -static int scc_get_CD(void * ptr); +static int scc_carrier_raised(struct tty_port *port); static void scc_shutdown_port(void * ptr); static int scc_set_real_termios(void *ptr); static void scc_hungup(void *ptr); @@ -100,7 +100,6 @@ static struct real_driver scc_real_driver = { scc_enable_tx_interrupts, scc_disable_rx_interrupts, scc_enable_rx_interrupts, - scc_get_CD, scc_shutdown_port, scc_set_real_termios, scc_chars_in_buffer, @@ -129,6 +128,10 @@ static const struct tty_operations scc_ops = { .break_ctl = scc_break_ctl, }; +static const struct tty_port_operations scc_port_ops = { + .carrier_raised = scc_carrier_raised, +}; + /*---------------------------------------------------------------------------- * vme_scc_init() and support functions *---------------------------------------------------------------------------*/ @@ -176,6 +179,8 @@ static void scc_init_portstructs(void) for (i = 0; i < 2; i++) { port = scc_ports + i; + tty_port_init(&port->gs.port); + port->gs.port.ops = &scc_port_ops; port->gs.magic = SCC_MAGIC; port->gs.close_delay = HZ/2; port->gs.closing_wait = 30 * HZ; @@ -624,9 +629,9 @@ static void scc_enable_rx_interrupts(void *ptr) } -static int scc_get_CD(void *ptr) +static int scc_carrier_raised(struct tty_port *port) { - struct scc_port *port = ptr; + struct scc_port *scc = container_of(port, struct scc_port, gs.port); unsigned channel = port->channel; return !!(scc_last_status_reg[channel] & SR_DCD); @@ -896,7 +901,7 @@ static int scc_open (struct tty_struct * tty, struct file * filp) return retval; } - port->c_dcd = scc_get_CD (port); + port->c_dcd = tty_port_carrier_raised(&port->gs.port); scc_enable_rx_interrupts(port); diff --git a/include/linux/generic_serial.h b/include/linux/generic_serial.h index 4cc913939817..fadff28505bb 100644 --- a/include/linux/generic_serial.h +++ b/include/linux/generic_serial.h @@ -21,7 +21,6 @@ struct real_driver { void (*enable_tx_interrupts) (void *); void (*disable_rx_interrupts) (void *); void (*enable_rx_interrupts) (void *); - int (*get_CD) (void *); void (*shutdown_port) (void*); int (*set_real_termios) (void*); int (*chars_in_buffer) (void*); diff --git a/include/linux/tty.h b/include/linux/tty.h index bbbeaef99626..bc7bae78e22f 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -180,8 +180,16 @@ struct signal_struct; * until a hangup so don't use the wrong path. */ +struct tty_port; + +struct tty_port_operations { + /* Return 1 if the carrier is raised */ + int (*carrier_raised)(struct tty_port *port); +}; + struct tty_port { struct tty_struct *tty; /* Back pointer */ + const struct tty_port_operations *ops; /* Port operations */ spinlock_t lock; /* Lock protecting tty field */ int blocked_open; /* Waiting to open */ int count; /* Usage count */ @@ -427,6 +435,7 @@ extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); extern struct tty_struct *tty_port_tty_get(struct tty_port *port); extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); +extern int tty_port_carrier_raised(struct tty_port *port); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); -- cgit v1.2.3 From 5d951fb458f847e5485b5251597fbf326000bb3b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 2 Jan 2009 13:45:19 +0000 Subject: tty: Pull the dtr raise into tty port This moves another per device special out of what should be shared open wait paths into private methods Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/isicom.c | 13 ++++++++----- drivers/char/mxser.c | 17 +++++++++++++---- drivers/char/rocket.c | 14 ++++++++++---- drivers/char/synclink.c | 21 +++++++++++++++------ drivers/char/synclink_gt.c | 21 +++++++++++++++------ drivers/char/tty_port.c | 16 ++++++++++++++++ include/linux/tty.h | 2 ++ 7 files changed, 79 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index b3da4858fd4a..a449449e301c 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -328,11 +328,13 @@ static inline void drop_rts(struct isi_port *port) } /* card->lock MUST NOT be held */ -static inline void raise_dtr_rts(struct isi_port *port) + +static void isicom_raise_dtr_rts(struct tty_port *port) { - struct isi_board *card = port->card; + struct isi_port *ip = container_of(port, struct isi_port, port); + struct isi_board *card = ip->card; unsigned long base = card->base; - u16 channel = port->channel; + u16 channel = ip->channel; if (!lock_card(card)) return; @@ -340,7 +342,7 @@ static inline void raise_dtr_rts(struct isi_port *port) outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0f04, base); InterruptTheCard(base); - port->status |= (ISI_DTR | ISI_RTS); + ip->status |= (ISI_DTR | ISI_RTS); unlock_card(card); } @@ -881,7 +883,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, spin_unlock_irqrestore(&card->card_lock, flags); while (1) { - raise_dtr_rts(ip); + tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { @@ -1462,6 +1464,7 @@ static const struct tty_operations isicom_ops = { static const struct tty_port_operations isicom_port_ops = { .carrier_raised = isicom_carrier_raised, + .raise_dtr_rts = isicom_raise_dtr_rts, }; static int __devinit reset_card(struct pci_dev *pdev, diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index eafbbcf355e7..ff5ff6188809 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -547,6 +547,17 @@ static int mxser_carrier_raised(struct tty_port *port) return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0; } +static void mxser_raise_dtr_rts(struct tty_port *port) +{ + struct mxser_port *mp = container_of(port, struct mxser_port, port); + unsigned long flags; + + spin_lock_irqsave(&mp->slock, flags); + outb(inb(mp->ioaddr + UART_MCR) | + UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR); + spin_unlock_irqrestore(&mp->slock, flags); +} + static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, struct mxser_port *mp) { @@ -586,10 +597,7 @@ static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, spin_unlock_irqrestore(&mp->slock, flags); port->blocked_open++; while (1) { - spin_lock_irqsave(&mp->slock, flags); - outb(inb(mp->ioaddr + UART_MCR) | - UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR); - spin_unlock_irqrestore(&mp->slock, flags); + tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { if (port->flags & ASYNC_HUP_NOTIFY) @@ -2458,6 +2466,7 @@ static const struct tty_operations mxser_ops = { struct tty_port_operations mxser_port_ops = { .carrier_raised = mxser_carrier_raised, + .raise_dtr_rts = mxser_raise_dtr_rts, }; /* diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 4a4110e703a5..f4d9c3993488 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -870,6 +870,13 @@ static int carrier_raised(struct tty_port *port) return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0; } +static void raise_dtr_rts(struct tty_port *port) +{ + struct r_port *info = container_of(port, struct r_port, port); + sSetDTR(&info->channel); + sSetRTS(&info->channel); +} + /* info->port.count is considered critical, protected by spinlocks. */ static int block_til_ready(struct tty_struct *tty, struct file *filp, struct r_port *info) @@ -928,10 +935,8 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, spin_unlock_irqrestore(&info->slock, flags); while (1) { - if (tty->termios->c_cflag & CBAUD) { - sSetDTR(&info->channel); - sSetRTS(&info->channel); - } + if (tty->termios->c_cflag & CBAUD) + tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ROCKET_INITIALIZED)) { if (info->flags & ROCKET_HUP_NOTIFY) @@ -2381,6 +2386,7 @@ static const struct tty_operations rocket_ops = { static const struct tty_port_operations rocket_port_ops = { .carrier_raised = carrier_raised, + .raise_dtr_rts = raise_dtr_rts, }; /* diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index fb2e6b5e0ef1..ac9f21e18c3f 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -3298,6 +3298,18 @@ static int carrier_raised(struct tty_port *port) return (info->serial_signals & SerialSignal_DCD) ? 1 : 0; } +static void raise_dtr_rts(struct tty_port *port) +{ + struct mgsl_struct *info = container_of(port, struct mgsl_struct, port); + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); +} + + /* block_til_ready() * * Block the current process until the specified port @@ -3358,12 +3370,8 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, port->blocked_open++; while (1) { - if (tty->termios->c_cflag & CBAUD) { - spin_lock_irqsave(&info->irq_spinlock,flags); - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; - usc_set_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } + if (tty->termios->c_cflag & CBAUD) + tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); @@ -4321,6 +4329,7 @@ static void mgsl_add_device( struct mgsl_struct *info ) static const struct tty_port_operations mgsl_port_ops = { .carrier_raised = carrier_raised, + .raise_dtr_rts = raise_dtr_rts, }; diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 39ccaba8ca37..625c9bde3be8 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -3143,6 +3143,18 @@ static int carrier_raised(struct tty_port *port) return (info->signals & SerialSignal_DCD) ? 1 : 0; } +static void raise_dtr_rts(struct tty_port *port) +{ + unsigned long flags; + struct slgt_info *info = container_of(port, struct slgt_info, port); + + spin_lock_irqsave(&info->lock,flags); + info->signals |= SerialSignal_RTS + SerialSignal_DTR; + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); +} + + /* * block current process until the device is ready to open */ @@ -3187,12 +3199,8 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if ((tty->termios->c_cflag & CBAUD)) { - spin_lock_irqsave(&info->lock,flags); - info->signals |= SerialSignal_RTS + SerialSignal_DTR; - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - } + if ((tty->termios->c_cflag & CBAUD)) + tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); @@ -3455,6 +3463,7 @@ static void add_device(struct slgt_info *info) static const struct tty_port_operations slgt_port_ops = { .carrier_raised = carrier_raised, + .raise_dtr_rts = raise_dtr_rts, }; /* diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index f54e40cbf023..0557b6384747 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -111,3 +111,19 @@ int tty_port_carrier_raised(struct tty_port *port) return port->ops->carrier_raised(port); } EXPORT_SYMBOL(tty_port_carrier_raised); + +/** + * tty_port_raise_dtr_rts - Riase DTR/RTS + * @port: tty port + * + * Wrapper for the DTR/RTS raise logic. For the moment this is used + * to hide some internal details. This will eventually become entirely + * internal to the tty port. + */ + +void tty_port_raise_dtr_rts(struct tty_port *port) +{ + if (port->ops->raise_dtr_rts) + port->ops->raise_dtr_rts(port); +} +EXPORT_SYMBOL(tty_port_raise_dtr_rts); diff --git a/include/linux/tty.h b/include/linux/tty.h index bc7bae78e22f..5001bbcacff6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -185,6 +185,7 @@ struct tty_port; struct tty_port_operations { /* Return 1 if the carrier is raised */ int (*carrier_raised)(struct tty_port *port); + void (*raise_dtr_rts)(struct tty_port *port); }; struct tty_port { @@ -436,6 +437,7 @@ extern void tty_port_free_xmit_buf(struct tty_port *port); extern struct tty_struct *tty_port_tty_get(struct tty_port *port); extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); extern int tty_port_carrier_raised(struct tty_port *port); +extern void tty_port_raise_dtr_rts(struct tty_port *port); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); -- cgit v1.2.3 From 3e61696bdc2103107674b06d0daf30b76193e922 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 2 Jan 2009 13:45:26 +0000 Subject: isicom: redo locking to use tty port locks This helps set the basis for moving block_til_ready into common code. We also introduce a tty_port_hangup helper as this will also be generally needed. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/isicom.c | 35 +++++++++++++++-------------------- drivers/char/synclinkmp.c | 20 ++++++++++++++------ drivers/char/tty_port.c | 24 ++++++++++++++++++++++++ include/linux/tty.h | 1 + 4 files changed, 54 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index a449449e301c..db53db91ae4a 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -841,7 +841,6 @@ static int isicom_carrier_raised(struct tty_port *port) static int block_til_ready(struct tty_struct *tty, struct file *filp, struct isi_port *ip) { - struct isi_board *card = ip->card; struct tty_port *port = &ip->port; int do_clocal = 0, retval; unsigned long flags; @@ -876,11 +875,11 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, retval = 0; add_wait_queue(&port->open_wait, &wait); - spin_lock_irqsave(&card->card_lock, flags); + spin_lock_irqsave(&port->lock, flags); if (!tty_hung_up_p(filp)) port->count--; port->blocked_open++; - spin_unlock_irqrestore(&card->card_lock, flags); + spin_unlock_irqrestore(&port->lock, flags); while (1) { tty_port_raise_dtr_rts(port); @@ -905,14 +904,13 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } set_current_state(TASK_RUNNING); remove_wait_queue(&port->open_wait, &wait); - spin_lock_irqsave(&card->card_lock, flags); + spin_lock_irqsave(&port->lock, flags); if (!tty_hung_up_p(filp)) port->count++; port->blocked_open--; - spin_unlock_irqrestore(&card->card_lock, flags); - if (retval) - return retval; - port->flags |= ASYNC_NORMAL_ACTIVE; + if (retval == 0) + port->flags |= ASYNC_NORMAL_ACTIVE; + spin_unlock_irqrestore(&port->lock, flags); return 0; } @@ -1034,9 +1032,9 @@ static void isicom_close(struct tty_struct *tty, struct file *filp) pr_dbg("Close start!!!.\n"); - spin_lock_irqsave(&card->card_lock, flags); + spin_lock_irqsave(&port->port.lock, flags); if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&card->card_lock, flags); + spin_unlock_irqrestore(&port->port.lock, flags); return; } @@ -1054,12 +1052,12 @@ static void isicom_close(struct tty_struct *tty, struct file *filp) } if (port->port.count) { - spin_unlock_irqrestore(&card->card_lock, flags); + spin_unlock_irqrestore(&port->port.lock, flags); return; } port->port.flags |= ASYNC_CLOSING; tty->closing = 1; - spin_unlock_irqrestore(&card->card_lock, flags); + spin_unlock_irqrestore(&port->port.lock, flags); if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, port->port.closing_wait); @@ -1076,22 +1074,22 @@ static void isicom_close(struct tty_struct *tty, struct file *filp) isicom_flush_buffer(tty); tty_ldisc_flush(tty); - spin_lock_irqsave(&card->card_lock, flags); + spin_lock_irqsave(&port->port.lock, flags); tty->closing = 0; if (port->port.blocked_open) { - spin_unlock_irqrestore(&card->card_lock, flags); + spin_unlock_irqrestore(&port->port.lock, flags); if (port->port.close_delay) { pr_dbg("scheduling until time out.\n"); msleep_interruptible( jiffies_to_msecs(port->port.close_delay)); } - spin_lock_irqsave(&card->card_lock, flags); + spin_lock_irqsave(&port->port.lock, flags); wake_up_interruptible(&port->port.open_wait); } port->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); wake_up_interruptible(&port->port.close_wait); - spin_unlock_irqrestore(&card->card_lock, flags); + spin_unlock_irqrestore(&port->port.lock, flags); } /* write et all */ @@ -1430,10 +1428,7 @@ static void isicom_hangup(struct tty_struct *tty) isicom_shutdown_port(port); spin_unlock_irqrestore(&port->card->card_lock, flags); - port->port.count = 0; - port->port.flags &= ~ASYNC_NORMAL_ACTIVE; - tty_port_tty_set(&port->port, NULL); - wake_up_interruptible(&port->port.open_wait); + tty_port_hangup(&port->port); } diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index fcf1ec77450d..1f5c21ec4b14 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -3331,6 +3331,17 @@ static int carrier_raised(struct tty_port *port) return (info->serial_signals & SerialSignal_DCD) ? 1 : 0; } +static void raise_dtr_rts(struct tty_port *port) +{ + SLMP_INFO *info = container_of(port, SLMP_INFO, port); + unsigned long flags; + + spin_lock_irqsave(&info->lock,flags); + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); +} + /* Block the current process until the specified port is ready to open. */ static int block_til_ready(struct tty_struct *tty, struct file *filp, @@ -3381,12 +3392,8 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if ((tty->termios->c_cflag & CBAUD)) { - spin_lock_irqsave(&info->lock,flags); - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - } + if (tty->termios->c_cflag & CBAUD) + tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); @@ -3793,6 +3800,7 @@ static void add_device(SLMP_INFO *info) static const struct tty_port_operations port_ops = { .carrier_raised = carrier_raised, + .raise_dtr_rts = raise_dtr_rts, }; /* Allocate and initialize a device instance structure diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 0557b6384747..9f418bca4a22 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,29 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) } EXPORT_SYMBOL(tty_port_tty_set); +/** + * tty_port_hangup - hangup helper + * @port: tty port + * + * Perform port level tty hangup flag and count changes. Drop the tty + * reference. + */ + +void tty_port_hangup(struct tty_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + port->count = 0; + port->flags &= ~ASYNC_NORMAL_ACTIVE; + if (port->tty) + tty_kref_put(port->tty); + port->tty = NULL; + spin_unlock_irqrestore(&port->lock, flags); + wake_up_interruptible(&port->open_wait); +} +EXPORT_SYMBOL(tty_port_hangup); + /** * tty_port_carrier_raised - carrier raised check * @port: tty port diff --git a/include/linux/tty.h b/include/linux/tty.h index 5001bbcacff6..a1a93140e6e4 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -438,6 +438,7 @@ extern struct tty_struct *tty_port_tty_get(struct tty_port *port); extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); extern int tty_port_carrier_raised(struct tty_port *port); extern void tty_port_raise_dtr_rts(struct tty_port *port); +extern void tty_port_hangup(struct tty_port *port); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); -- cgit v1.2.3 From 36c621d82b956ff6ff72273f848af53e6c581aba Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 2 Jan 2009 13:46:10 +0000 Subject: tty: Introduce a tty_port generic block_til_ready Start sucking more commonality out of the drivers into a single piece of core code. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/isicom.c | 79 +----------------------------------- drivers/char/mxser.c | 71 +------------------------------- drivers/char/riscom8.c | 86 +-------------------------------------- drivers/char/synclink.c | 1 + drivers/char/tty_port.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/char/vme_scc.c | 6 +-- include/linux/tty.h | 2 + 7 files changed, 115 insertions(+), 235 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index db53db91ae4a..bac55cf44243 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -838,82 +838,6 @@ static int isicom_carrier_raised(struct tty_port *port) return (ip->status & ISI_DCD)?1 : 0; } -static int block_til_ready(struct tty_struct *tty, struct file *filp, - struct isi_port *ip) -{ - struct tty_port *port = &ip->port; - int do_clocal = 0, retval; - unsigned long flags; - DECLARE_WAITQUEUE(wait, current); - int cd; - - /* block if port is in the process of being closed */ - - if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - pr_dbg("block_til_ready: close in progress.\n"); - interruptible_sleep_on(&port->close_wait); - if (port->flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; - } - - /* if non-blocking mode is set ... */ - - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - pr_dbg("block_til_ready: non-block mode.\n"); - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (C_CLOCAL(tty)) - do_clocal = 1; - - /* block waiting for DCD to be asserted, and while - callout dev is busy */ - retval = 0; - add_wait_queue(&port->open_wait, &wait); - - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count--; - port->blocked_open++; - spin_unlock_irqrestore(&port->lock, flags); - - while (1) { - tty_port_raise_dtr_rts(port); - - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; - break; - } - cd = tty_port_carrier_raised(port); - if (!(port->flags & ASYNC_CLOSING) && - (do_clocal || cd)) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - if (retval == 0) - port->flags |= ASYNC_NORMAL_ACTIVE; - spin_unlock_irqrestore(&port->lock, flags); - return 0; -} - static int isicom_open(struct tty_struct *tty, struct file *filp) { struct isi_port *port; @@ -940,12 +864,13 @@ static int isicom_open(struct tty_struct *tty, struct file *filp) isicom_setup_board(card); + /* FIXME: locking on port.count etc */ port->port.count++; tty->driver_data = port; tty_port_tty_set(&port->port, tty); error = isicom_setup_port(tty); if (error == 0) - error = block_til_ready(tty, filp, port); + error = tty_port_block_til_ready(&port->port, tty, filp); return error; } diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index e2471cf1ee95..08ba6eb1a380 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -558,75 +558,6 @@ static void mxser_raise_dtr_rts(struct tty_port *port) spin_unlock_irqrestore(&mp->slock, flags); } -static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, - struct mxser_port *mp) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - unsigned long flags; - int cd; - struct tty_port *port = &mp->port; - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - test_bit(TTY_IO_ERROR, &tty->flags)) { - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, port->count is dropped by one, so that - * mxser_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&port->open_wait, &wait); - - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count--; - port->blocked_open++; - spin_unlock_irqrestore(&port->lock, flags); - while (1) { - tty_port_raise_dtr_rts(port); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; - break; - } - cd = tty_port_carrier_raised(port); - if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd)) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - if (retval == 0) - port->flags |= ASYNC_NORMAL_ACTIVE; - spin_unlock_irqrestore(&port->lock, flags); - return 0; -} - static int mxser_set_baud(struct tty_struct *tty, long newspd) { struct mxser_port *info = tty->driver_data; @@ -1110,7 +1041,7 @@ static int mxser_open(struct tty_struct *tty, struct file *filp) if (retval) return retval; - retval = mxser_block_til_ready(tty, filp, info); + retval = tty_port_block_til_ready(&info->port, tty, filp); if (retval) return retval; diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 14662d7aa628..af34c2054a09 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -874,90 +874,6 @@ static int carrier_raised(struct tty_port *port) return CD; } -static int block_til_ready(struct tty_struct *tty, struct file *filp, - struct riscom_port *rp) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - int CD; - unsigned long flags; - struct tty_port *port = &rp->port; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&port->close_wait); - if (port->flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (C_CLOCAL(tty)) - do_clocal = 1; - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * rs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&port->open_wait, &wait); - - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count--; - port->blocked_open++; - spin_unlock_irqrestore(&port->lock, flags); - - while (1) { - - CD = tty_port_carrier_raised(port); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; - break; - } - if (!(port->flags & ASYNC_CLOSING) && - (do_clocal || CD)) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - if (retval == 0) - port->flags |= ASYNC_NORMAL_ACTIVE; - spin_unlock_irqrestore(&port->lock, flags); - return 0; -} - static int rc_open(struct tty_struct *tty, struct file *filp) { int board; @@ -984,7 +900,7 @@ static int rc_open(struct tty_struct *tty, struct file *filp) error = rc_setup_port(bp, port); if (error == 0) - error = block_til_ready(tty, filp, port); + error = tty_port_block_til_ready(&port->port, tty, filp); return error; } diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index ac9f21e18c3f..0ded4ed3da3c 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -3401,6 +3401,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, set_current_state(TASK_RUNNING); remove_wait_queue(&port->open_wait, &wait); + /* FIXME: Racy on hangup during close wait */ if (extra_count) port->count++; port->blocked_open--; diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 9f418bca4a22..ff94182b3813 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -151,3 +151,108 @@ void tty_port_raise_dtr_rts(struct tty_port *port) port->ops->raise_dtr_rts(port); } EXPORT_SYMBOL(tty_port_raise_dtr_rts); + +/** + * tty_port_block_til_ready - Waiting logic for tty open + * @port: the tty port being opened + * @tty: the tty device being bound + * @filp: the file pointer of the opener + * + * Implement the core POSIX/SuS tty behaviour when opening a tty device. + * Handles: + * - hangup (both before and during) + * - non blocking open + * - rts/dtr/dcd + * - signals + * - port flags and counts + * + * The passed tty_port must implement the carrier_raised method if it can + * do carrier detect and the raise_dtr_rts method if it supports software + * management of these lines. Note that the dtr/rts raise is done each + * iteration as a hangup may have previously dropped them while we wait. + */ + +int tty_port_block_til_ready(struct tty_port *port, + struct tty_struct *tty, struct file *filp) +{ + int do_clocal = 0, retval; + unsigned long flags; + DECLARE_WAITQUEUE(wait, current); + int cd; + + /* block if port is in the process of being closed */ + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + /* if non-blocking mode is set we can pass directly to open unless + the port has just hung up or is in another error state */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (C_CLOCAL(tty)) + do_clocal = 1; + + /* Block waiting until we can proceed. We may need to wait for the + carrier, but we must also wait for any close that is in progress + before the next open may complete */ + + retval = 0; + add_wait_queue(&port->open_wait, &wait); + + /* The port lock protects the port counts */ + spin_lock_irqsave(&port->lock, flags); + if (!tty_hung_up_p(filp)) + port->count--; + port->blocked_open++; + spin_unlock_irqrestore(&port->lock, flags); + + while (1) { + /* Indicate we are open */ + tty_port_raise_dtr_rts(port); + + set_current_state(TASK_INTERRUPTIBLE); + /* Check for a hangup or uninitialised port. Return accordingly */ + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + /* Probe the carrier. For devices with no carrier detect this + will always return true */ + cd = tty_port_carrier_raised(port); + if (!(port->flags & ASYNC_CLOSING) && + (do_clocal || cd)) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->open_wait, &wait); + + /* Update counts. A parallel hangup will have set count to zero and + we must not mess that up further */ + spin_lock_irqsave(&port->lock, flags); + if (!tty_hung_up_p(filp)) + port->count++; + port->blocked_open--; + if (retval == 0) + port->flags |= ASYNC_NORMAL_ACTIVE; + spin_unlock_irqrestore(&port->lock, flags); + return 0; + +} +EXPORT_SYMBOL(tty_port_block_til_ready); + diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c index d4e1534c0e03..2d9242a45a0d 100644 --- a/drivers/char/vme_scc.c +++ b/drivers/char/vme_scc.c @@ -631,8 +631,8 @@ static void scc_enable_rx_interrupts(void *ptr) static int scc_carrier_raised(struct tty_port *port) { - struct scc_port *scc = container_of(port, struct scc_port, gs.port); - unsigned channel = port->channel; + struct scc_port *sc = container_of(port, struct scc_port, gs.port); + unsigned channel = sc->channel; return !!(scc_last_status_reg[channel] & SR_DCD); } @@ -643,7 +643,7 @@ static void scc_shutdown_port(void *ptr) struct scc_port *port = ptr; port->gs.port.flags &= ~ GS_ACTIVE; - if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL) { + if (port->gs.port.tty && (port->gs.port.tty->termios->c_cflag & HUPCL)) { scc_setsignals (port, 0, 0); } } diff --git a/include/linux/tty.h b/include/linux/tty.h index a1a93140e6e4..61a0ab32cf11 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -439,6 +439,8 @@ extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); extern int tty_port_carrier_raised(struct tty_port *port); extern void tty_port_raise_dtr_rts(struct tty_port *port); extern void tty_port_hangup(struct tty_port *port); +extern int tty_port_block_til_ready(struct tty_port *port, + struct tty_struct *tty, struct file *filp); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); -- cgit v1.2.3 From 2a6eadbd5a2ae8f458e421f3614f1ad13c0f9a1c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 2 Jan 2009 13:46:18 +0000 Subject: tty: Rework istallion to use the tty port changes Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/istallion.c | 159 +++++++++++++--------------------------------- include/linux/istallion.h | 1 - 2 files changed, 43 insertions(+), 117 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index c4682f9e34bb..4c69ab97339a 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -626,8 +626,6 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp); static int stli_initopen(struct tty_struct *tty, struct stlibrd *brdp, struct stliport *portp); static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); -static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp, - struct stliport *portp, struct file *filp); static int stli_setport(struct tty_struct *tty); static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); @@ -787,6 +785,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) { struct stlibrd *brdp; struct stliport *portp; + struct tty_port *port; unsigned int minordev, brdnr, portnr; int rc; @@ -808,30 +807,19 @@ static int stli_open(struct tty_struct *tty, struct file *filp) return -ENODEV; if (portp->devnr < 1) return -ENODEV; - - -/* - * Check if this port is in the middle of closing. If so then wait - * until it is closed then return error status based on flag settings. - * The sleep here does not need interrupt protection since the wakeup - * for it is done with the same context. - */ - if (portp->port.flags & ASYNC_CLOSING) { - interruptible_sleep_on(&portp->port.close_wait); - if (portp->port.flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - return -ERESTARTSYS; - } + port = &portp->port; /* * On the first open of the device setup the port hardware, and * initialize the per port data structure. Since initializing the port * requires several commands to the board we will need to wait for any * other open that is already initializing the port. + * + * Review - locking */ - tty_port_tty_set(&portp->port, tty); + tty_port_tty_set(port, tty); tty->driver_data = portp; - portp->port.count++; + port->count++; wait_event_interruptible(portp->raw_wait, !test_bit(ST_INITIALIZING, &portp->state)); @@ -841,7 +829,8 @@ static int stli_open(struct tty_struct *tty, struct file *filp) if ((portp->port.flags & ASYNC_INITIALIZED) == 0) { set_bit(ST_INITIALIZING, &portp->state); if ((rc = stli_initopen(tty, brdp, portp)) >= 0) { - portp->port.flags |= ASYNC_INITIALIZED; + /* Locking */ + port->flags |= ASYNC_INITIALIZED; clear_bit(TTY_IO_ERROR, &tty->flags); } clear_bit(ST_INITIALIZING, &portp->state); @@ -849,31 +838,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) if (rc < 0) return rc; } - -/* - * Check if this port is in the middle of closing. If so then wait - * until it is closed then return error status, based on flag settings. - * The sleep here does not need interrupt protection since the wakeup - * for it is done with the same context. - */ - if (portp->port.flags & ASYNC_CLOSING) { - interruptible_sleep_on(&portp->port.close_wait); - if (portp->port.flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - return -ERESTARTSYS; - } - -/* - * Based on type of open being done check if it can overlap with any - * previous opens still in effect. If we are a normal serial device - * then also we might have to wait for carrier. - */ - if (!(filp->f_flags & O_NONBLOCK)) { - if ((rc = stli_waitcarrier(tty, brdp, portp, filp)) != 0) - return rc; - } - portp->port.flags |= ASYNC_NORMAL_ACTIVE; - return 0; + return tty_port_block_til_ready(&portp->port, tty, filp); } /*****************************************************************************/ @@ -882,25 +847,29 @@ static void stli_close(struct tty_struct *tty, struct file *filp) { struct stlibrd *brdp; struct stliport *portp; + struct tty_port *port; unsigned long flags; portp = tty->driver_data; if (portp == NULL) return; + port = &portp->port; - spin_lock_irqsave(&stli_lock, flags); + spin_lock_irqsave(&port->lock, flags); if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&stli_lock, flags); + spin_unlock_irqrestore(&port->lock, flags); return; } - if ((tty->count == 1) && (portp->port.count != 1)) - portp->port.count = 1; - if (portp->port.count-- > 1) { - spin_unlock_irqrestore(&stli_lock, flags); + if (tty->count == 1 && port->count != 1) + port->count = 1; + if (port->count-- > 1) { + spin_unlock_irqrestore(&port->lock, flags); return; } - portp->port.flags |= ASYNC_CLOSING; + port->flags |= ASYNC_CLOSING; + tty->closing = 1; + spin_unlock_irqrestore(&port->lock, flags); /* * May want to wait for data to drain before closing. The BUSY flag @@ -908,15 +877,17 @@ static void stli_close(struct tty_struct *tty, struct file *filp) * updated by messages from the slave - indicating when all chars * really have drained. */ + spin_lock_irqsave(&stli_lock, flags); if (tty == stli_txcooktty) stli_flushchars(tty); - tty->closing = 1; spin_unlock_irqrestore(&stli_lock, flags); if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, portp->closing_wait); - portp->port.flags &= ~ASYNC_INITIALIZED; + /* FIXME: port locking here needs attending to */ + port->flags &= ~ASYNC_INITIALIZED; + brdp = stli_brds[portp->brdnr]; stli_rawclose(brdp, portp, 0, 0); if (tty->termios->c_cflag & HUPCL) { @@ -937,14 +908,14 @@ static void stli_close(struct tty_struct *tty, struct file *filp) tty->closing = 0; tty_port_tty_set(&portp->port, NULL); - if (portp->openwaitcnt) { + if (port->blocked_open) { if (portp->close_delay) msleep_interruptible(jiffies_to_msecs(portp->close_delay)); - wake_up_interruptible(&portp->port.open_wait); + wake_up_interruptible(&port->open_wait); } - portp->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&portp->port.close_wait); + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&port->close_wait); } /*****************************************************************************/ @@ -1189,63 +1160,17 @@ static int stli_carrier_raised(struct tty_port *port) return (portp->sigs & TIOCM_CD) ? 1 : 0; } -/* - * Possibly need to wait for carrier (DCD signal) to come high. Say - * maybe because if we are clocal then we don't need to wait... - */ - -static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp, - struct stliport *portp, struct file *filp) +static void stli_raise_dtr_rts(struct tty_port *port) { - unsigned long flags; - int rc, doclocal; - struct tty_port *port = &portp->port; - - rc = 0; - doclocal = 0; - - if (tty->termios->c_cflag & CLOCAL) - doclocal++; - - spin_lock_irqsave(&stli_lock, flags); - portp->openwaitcnt++; - if (! tty_hung_up_p(filp)) - port->count--; - spin_unlock_irqrestore(&stli_lock, flags); - - for (;;) { - stli_mkasysigs(&portp->asig, 1, 1); - if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, - &portp->asig, sizeof(asysigs_t), 0)) < 0) - break; - if (tty_hung_up_p(filp) || - ((port->flags & ASYNC_INITIALIZED) == 0)) { - if (port->flags & ASYNC_HUP_NOTIFY) - rc = -EBUSY; - else - rc = -ERESTARTSYS; - break; - } - if (((port->flags & ASYNC_CLOSING) == 0) && - (doclocal || tty_port_carrier_raised(port))) { - break; - } - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - interruptible_sleep_on(&port->open_wait); - } - - spin_lock_irqsave(&stli_lock, flags); - if (! tty_hung_up_p(filp)) - port->count++; - portp->openwaitcnt--; - spin_unlock_irqrestore(&stli_lock, flags); - - return rc; + struct stliport *portp = container_of(port, struct stliport, port); + struct stlibrd *brdp = stli_brds[portp->brdnr]; + stli_mkasysigs(&portp->asig, 1, 1); + if (stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0) < 0) + printk(KERN_WARNING "istallion: dtr raise failed.\n"); } + /*****************************************************************************/ /* @@ -1828,6 +1753,7 @@ static void stli_hangup(struct tty_struct *tty) { struct stliport *portp; struct stlibrd *brdp; + struct tty_port *port; unsigned long flags; portp = tty->driver_data; @@ -1838,8 +1764,11 @@ static void stli_hangup(struct tty_struct *tty) brdp = stli_brds[portp->brdnr]; if (brdp == NULL) return; + port = &portp->port; - portp->port.flags &= ~ASYNC_INITIALIZED; + spin_lock_irqsave(&port->lock, flags); + port->flags &= ~ASYNC_INITIALIZED; + spin_unlock_irqrestore(&port->lock, flags); if (!test_bit(ST_CLOSING, &portp->state)) stli_rawclose(brdp, portp, 0, 0); @@ -1860,12 +1789,9 @@ static void stli_hangup(struct tty_struct *tty) clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); set_bit(TTY_IO_ERROR, &tty->flags); - tty_port_tty_set(&portp->port, NULL); - portp->port.flags &= ~ASYNC_NORMAL_ACTIVE; - portp->port.count = 0; spin_unlock_irqrestore(&stli_lock, flags); - wake_up_interruptible(&portp->port.open_wait); + tty_port_hangup(port); } /*****************************************************************************/ @@ -4528,6 +4454,7 @@ static const struct tty_operations stli_ops = { static const struct tty_port_operations stli_port_ops = { .carrier_raised = stli_carrier_raised, + .raise_dtr_rts = stli_raise_dtr_rts, }; /*****************************************************************************/ diff --git a/include/linux/istallion.h b/include/linux/istallion.h index 0d1840723249..053d5aea925c 100644 --- a/include/linux/istallion.h +++ b/include/linux/istallion.h @@ -61,7 +61,6 @@ struct stliport { int custom_divisor; int close_delay; int closing_wait; - int openwaitcnt; int rc; int argsize; void *argp; -- cgit v1.2.3 From a6614999e800cf3a134ce93ea46ef837e3c0e76e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 2 Jan 2009 13:46:50 +0000 Subject: tty: Introduce some close helpers for ports Again this is a lot of common code we can unify Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/isicom.c | 66 ++++++--------------------------------- drivers/char/istallion.c | 78 +++++++++++++++++----------------------------- drivers/char/mxser.c | 56 ++++++--------------------------- drivers/char/riscom8.c | 49 +++-------------------------- drivers/char/stallion.c | 39 +++-------------------- drivers/char/synclink.c | 58 ++-------------------------------- drivers/char/synclink_gt.c | 51 ++---------------------------- drivers/char/synclinkmp.c | 58 ++-------------------------------- drivers/char/tty_port.c | 58 ++++++++++++++++++++++++++++++++++ include/linux/istallion.h | 1 - include/linux/tty.h | 3 ++ 11 files changed, 126 insertions(+), 391 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index bac55cf44243..24aa6e88e223 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -945,76 +945,30 @@ static void isicom_flush_buffer(struct tty_struct *tty) static void isicom_close(struct tty_struct *tty, struct file *filp) { - struct isi_port *port = tty->driver_data; + struct isi_port *ip = tty->driver_data; + struct tty_port *port = &ip->port; struct isi_board *card; unsigned long flags; - if (!port) - return; - card = port->card; - if (isicom_paranoia_check(port, tty->name, "isicom_close")) - return; - - pr_dbg("Close start!!!.\n"); - - spin_lock_irqsave(&port->port.lock, flags); - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&port->port.lock, flags); - return; - } - - if (tty->count == 1 && port->port.count != 1) { - printk(KERN_WARNING "ISICOM:(0x%lx) isicom_close: bad port " - "count tty->count = 1 port count = %d.\n", - card->base, port->port.count); - port->port.count = 1; - } - if (--port->port.count < 0) { - printk(KERN_WARNING "ISICOM:(0x%lx) isicom_close: bad port " - "count for channel%d = %d", card->base, port->channel, - port->port.count); - port->port.count = 0; - } + BUG_ON(!ip); - if (port->port.count) { - spin_unlock_irqrestore(&port->port.lock, flags); + card = ip->card; + if (isicom_paranoia_check(ip, tty->name, "isicom_close")) return; - } - port->port.flags |= ASYNC_CLOSING; - tty->closing = 1; - spin_unlock_irqrestore(&port->port.lock, flags); - if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, port->port.closing_wait); /* indicate to the card that no more data can be received on this port */ spin_lock_irqsave(&card->card_lock, flags); - if (port->port.flags & ASYNC_INITIALIZED) { - card->port_status &= ~(1 << port->channel); + if (port->flags & ASYNC_INITIALIZED) { + card->port_status &= ~(1 << ip->channel); outw(card->port_status, card->base + 0x02); } - isicom_shutdown_port(port); + isicom_shutdown_port(ip); spin_unlock_irqrestore(&card->card_lock, flags); isicom_flush_buffer(tty); - tty_ldisc_flush(tty); - - spin_lock_irqsave(&port->port.lock, flags); - tty->closing = 0; - - if (port->port.blocked_open) { - spin_unlock_irqrestore(&port->port.lock, flags); - if (port->port.close_delay) { - pr_dbg("scheduling until time out.\n"); - msleep_interruptible( - jiffies_to_msecs(port->port.close_delay)); - } - spin_lock_irqsave(&port->port.lock, flags); - wake_up_interruptible(&port->port.open_wait); - } - port->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - wake_up_interruptible(&port->port.close_wait); - spin_unlock_irqrestore(&port->port.lock, flags); + + tty_port_close_end(port, tty); } /* write et all */ diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 4c69ab97339a..5c3dc6b8411c 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -767,7 +767,7 @@ static int stli_parsebrd(struct stlconf *confp, char **argp) break; } if (i == ARRAY_SIZE(stli_brdstr)) { - printk("STALLION: unknown board name, %s?\n", argp[0]); + printk(KERN_WARNING "istallion: unknown board name, %s?\n", argp[0]); return 0; } @@ -855,21 +855,8 @@ static void stli_close(struct tty_struct *tty, struct file *filp) return; port = &portp->port; - spin_lock_irqsave(&port->lock, flags); - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&port->lock, flags); - return; - } - if (tty->count == 1 && port->count != 1) - port->count = 1; - if (port->count-- > 1) { - spin_unlock_irqrestore(&port->lock, flags); + if (tty_port_close_start(port, tty, filp) == 0) return; - } - - port->flags |= ASYNC_CLOSING; - tty->closing = 1; - spin_unlock_irqrestore(&port->lock, flags); /* * May want to wait for data to drain before closing. The BUSY flag @@ -882,6 +869,8 @@ static void stli_close(struct tty_struct *tty, struct file *filp) stli_flushchars(tty); spin_unlock_irqrestore(&stli_lock, flags); + /* We end up doing this twice for the moment. This needs looking at + eventually. Note we still use portp->closing_wait as a result */ if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, portp->closing_wait); @@ -905,17 +894,8 @@ static void stli_close(struct tty_struct *tty, struct file *filp) set_bit(ST_DOFLUSHRX, &portp->state); stli_flushbuffer(tty); - tty->closing = 0; - tty_port_tty_set(&portp->port, NULL); - - if (port->blocked_open) { - if (portp->close_delay) - msleep_interruptible(jiffies_to_msecs(portp->close_delay)); - wake_up_interruptible(&port->open_wait); - } - - port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&port->close_wait); + tty_port_close_end(port, tty); + tty_port_tty_set(port, NULL); } /*****************************************************************************/ @@ -1482,7 +1462,7 @@ static int stli_getserial(struct stliport *portp, struct serial_struct __user *s sio.irq = 0; sio.flags = portp->port.flags; sio.baud_base = portp->baud_base; - sio.close_delay = portp->close_delay; + sio.close_delay = portp->port.close_delay; sio.closing_wait = portp->closing_wait; sio.custom_divisor = portp->custom_divisor; sio.xmit_fifo_size = 0; @@ -1514,7 +1494,7 @@ static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *s return -EFAULT; if (!capable(CAP_SYS_ADMIN)) { if ((sio.baud_base != portp->baud_base) || - (sio.close_delay != portp->close_delay) || + (sio.close_delay != portp->port.close_delay) || ((sio.flags & ~ASYNC_USR_MASK) != (portp->port.flags & ~ASYNC_USR_MASK))) return -EPERM; @@ -1523,7 +1503,7 @@ static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *s portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; - portp->close_delay = sio.close_delay; + portp->port.close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; portp->custom_divisor = sio.custom_divisor; @@ -2065,7 +2045,7 @@ static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigne unsigned char __iomem *bits; if (test_bit(ST_CMDING, &portp->state)) { - printk(KERN_ERR "STALLION: command already busy, cmd=%x!\n", + printk(KERN_ERR "istallion: command already busy, cmd=%x!\n", (int) cmd); return; } @@ -2625,7 +2605,7 @@ static int stli_initports(struct stlibrd *brdp) for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) { portp = kzalloc(sizeof(struct stliport), GFP_KERNEL); if (!portp) { - printk("STALLION: failed to allocate port structure\n"); + printk(KERN_WARNING "istallion: failed to allocate port structure\n"); continue; } tty_port_init(&portp->port); @@ -2635,7 +2615,7 @@ static int stli_initports(struct stlibrd *brdp) portp->brdnr = brdp->brdnr; portp->panelnr = panelnr; portp->baud_base = STL_BAUDBASE; - portp->close_delay = STL_CLOSEDELAY; + portp->port.close_delay = STL_CLOSEDELAY; portp->closing_wait = 30 * HZ; init_waitqueue_head(&portp->port.open_wait); init_waitqueue_head(&portp->port.close_wait); @@ -2692,7 +2672,7 @@ static void __iomem *stli_ecpgetmemptr(struct stlibrd *brdp, unsigned long offse unsigned char val; if (offset > brdp->memsize) { - printk(KERN_ERR "STALLION: shared memory pointer=%x out of " + printk(KERN_ERR "istallion: shared memory pointer=%x out of " "range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = NULL; @@ -2766,7 +2746,7 @@ static void __iomem *stli_ecpeigetmemptr(struct stlibrd *brdp, unsigned long off unsigned char val; if (offset > brdp->memsize) { - printk(KERN_ERR "STALLION: shared memory pointer=%x out of " + printk(KERN_ERR "istallion: shared memory pointer=%x out of " "range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = NULL; @@ -2818,7 +2798,7 @@ static void __iomem *stli_ecpmcgetmemptr(struct stlibrd *brdp, unsigned long off unsigned char val; if (offset > brdp->memsize) { - printk(KERN_ERR "STALLION: shared memory pointer=%x out of " + printk(KERN_ERR "istallion: shared memory pointer=%x out of " "range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = NULL; @@ -2863,7 +2843,7 @@ static void __iomem *stli_ecppcigetmemptr(struct stlibrd *brdp, unsigned long of unsigned char val; if (offset > brdp->memsize) { - printk(KERN_ERR "STALLION: shared memory pointer=%x out of " + printk(KERN_ERR "istallion: shared memory pointer=%x out of " "range at line=%d(%d), board=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = NULL; @@ -2928,7 +2908,7 @@ static void __iomem *stli_onbgetmemptr(struct stlibrd *brdp, unsigned long offse void __iomem *ptr; if (offset > brdp->memsize) { - printk(KERN_ERR "STALLION: shared memory pointer=%x out of " + printk(KERN_ERR "istallion: shared memory pointer=%x out of " "range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = NULL; @@ -2994,7 +2974,7 @@ static void __iomem *stli_onbegetmemptr(struct stlibrd *brdp, unsigned long offs unsigned char val; if (offset > brdp->memsize) { - printk(KERN_ERR "STALLION: shared memory pointer=%x out of " + printk(KERN_ERR "istallion: shared memory pointer=%x out of " "range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = NULL; @@ -3433,7 +3413,7 @@ static int stli_startbrd(struct stlibrd *brdp) #endif if (nrdevs < (brdp->nrports + 1)) { - printk(KERN_ERR "STALLION: slave failed to allocate memory for " + printk(KERN_ERR "istallion: slave failed to allocate memory for " "all devices, devices=%d\n", nrdevs); brdp->nrports = nrdevs - 1; } @@ -3443,13 +3423,13 @@ static int stli_startbrd(struct stlibrd *brdp) brdp->bitsize = (nrdevs + 7) / 8; memoff = readl(&hdrp->memp); if (memoff > brdp->memsize) { - printk(KERN_ERR "STALLION: corrupted shared memory region?\n"); + printk(KERN_ERR "istallion: corrupted shared memory region?\n"); rc = -EIO; goto stli_donestartup; } memp = (cdkmem_t __iomem *) EBRDGETMEMPTR(brdp, memoff); if (readw(&memp->dtype) != TYP_ASYNCTRL) { - printk(KERN_ERR "STALLION: no slave control device found\n"); + printk(KERN_ERR "istallion: no slave control device found\n"); goto stli_donestartup; } memp++; @@ -3534,7 +3514,7 @@ static int __devinit stli_brdinit(struct stlibrd *brdp) retval = stli_initonb(brdp); break; default: - printk(KERN_ERR "STALLION: board=%d is unknown board " + printk(KERN_ERR "istallion: board=%d is unknown board " "type=%d\n", brdp->brdnr, brdp->brdtype); retval = -ENODEV; } @@ -3543,7 +3523,7 @@ static int __devinit stli_brdinit(struct stlibrd *brdp) return retval; stli_initports(brdp); - printk(KERN_INFO "STALLION: %s found, board=%d io=%x mem=%x " + printk(KERN_INFO "istallion: %s found, board=%d io=%x mem=%x " "nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr, brdp->nrpanels, brdp->nrports); @@ -3637,7 +3617,7 @@ static int stli_eisamemprobe(struct stlibrd *brdp) if (! foundit) { brdp->memaddr = 0; brdp->membase = NULL; - printk(KERN_ERR "STALLION: failed to probe shared memory " + printk(KERN_ERR "istallion: failed to probe shared memory " "region for %s in EISA slot=%d\n", stli_brdnames[brdp->brdtype], (brdp->iobase >> 12)); return -ENODEV; @@ -3782,7 +3762,7 @@ static int __devinit stli_pciprobe(struct pci_dev *pdev, mutex_lock(&stli_brdslock); brdnr = stli_getbrdnr(); if (brdnr < 0) { - printk(KERN_INFO "STALLION: too many boards found, " + printk(KERN_INFO "istallion: too many boards found, " "maximum supported %d\n", STL_MAXBRDS); mutex_unlock(&stli_brdslock); retval = -EIO; @@ -3854,7 +3834,7 @@ static struct stlibrd *stli_allocbrd(void) brdp = kzalloc(sizeof(struct stlibrd), GFP_KERNEL); if (!brdp) { - printk(KERN_ERR "STALLION: failed to allocate memory " + printk(KERN_ERR "istallion: failed to allocate memory " "(size=%Zd)\n", sizeof(struct stlibrd)); return NULL; } @@ -4493,7 +4473,7 @@ static int __init istallion_module_init(void) stli_txcookbuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL); if (!stli_txcookbuf) { - printk(KERN_ERR "STALLION: failed to allocate memory " + printk(KERN_ERR "istallion: failed to allocate memory " "(size=%d)\n", STLI_TXBUFSIZE); retval = -ENOMEM; goto err; @@ -4518,7 +4498,7 @@ static int __init istallion_module_init(void) retval = tty_register_driver(stli_serial); if (retval) { - printk(KERN_ERR "STALLION: failed to register serial driver\n"); + printk(KERN_ERR "istallion: failed to register serial driver\n"); goto err_ttyput; } @@ -4532,7 +4512,7 @@ static int __init istallion_module_init(void) */ retval = register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem); if (retval) { - printk(KERN_ERR "STALLION: failed to register serial memory " + printk(KERN_ERR "istallion: failed to register serial memory " "device\n"); goto err_deinit; } diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 08ba6eb1a380..402c9f217f83 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -1080,57 +1080,26 @@ static void mxser_flush_buffer(struct tty_struct *tty) static void mxser_close(struct tty_struct *tty, struct file *filp) { struct mxser_port *info = tty->driver_data; + struct tty_port *port = &info->port; unsigned long timeout; - unsigned long flags; if (tty->index == MXSER_PORTS) return; if (!info) return; - spin_lock_irqsave(&info->port.lock, flags); - - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&info->port.lock, flags); - return; - } - if ((tty->count == 1) && (info->port.count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->port.count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk(KERN_ERR "mxser_close: bad serial port count; " - "tty->count is 1, info->port.count is %d\n", info->port.count); - info->port.count = 1; - } - if (--info->port.count < 0) { - printk(KERN_ERR "mxser_close: bad serial port count for " - "ttys%d: %d\n", tty->index, info->port.count); - info->port.count = 0; - } - if (info->port.count) { - spin_unlock_irqrestore(&info->port.lock, flags); + if (tty_port_close_start(port, tty, filp) == 0) return; - } - info->port.flags |= ASYNC_CLOSING; - spin_unlock_irqrestore(&info->port.lock, flags); + /* * Save the termios structure, since this port may have * separate termios for callout and dialin. + * + * FIXME: Can this go ? */ if (info->port.flags & ASYNC_NORMAL_ACTIVE) info->normal_termios = *tty->termios; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->port.closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the @@ -1156,19 +1125,12 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) } } mxser_shutdown(tty); - mxser_flush_buffer(tty); - tty_ldisc_flush(tty); - - tty->closing = 0; - tty_port_tty_set(&info->port, NULL); - if (info->port.blocked_open) { - if (info->port.close_delay) - schedule_timeout_interruptible(info->port.close_delay); - wake_up_interruptible(&info->port.open_wait); - } - info->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + /* Right now the tty_port set is done outside of the close_end helper + as we don't yet have everyone using refcounts */ + tty_port_close_end(port, tty); + tty_port_tty_set(port, NULL); } static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count) diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index af34c2054a09..9ac5febd8abd 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -929,35 +929,11 @@ static void rc_close(struct tty_struct *tty, struct file *filp) if (!port || rc_paranoia_check(port, tty->name, "close")) return; - spin_lock_irqsave(&port->port.lock, flags); - - if (tty_hung_up_p(filp)) - goto out; - bp = port_Board(port); - if ((tty->count == 1) && (port->port.count != 1)) { - printk(KERN_INFO "rc%d: rc_close: bad port count;" - " tty->count is 1, port count is %d\n", - board_No(bp), port->port.count); - port->port.count = 1; - } - if (--port->port.count < 0) { - printk(KERN_INFO "rc%d: rc_close: bad port count " - "for tty%d: %d\n", - board_No(bp), port_No(port), port->port.count); - port->port.count = 0; - } - if (port->port.count) - goto out; - port->port.flags |= ASYNC_CLOSING; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - spin_unlock_irqrestore(&port->port.lock, flags); - if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, port->port.closing_wait); + + if (tty_port_close_start(&port->port, tty, filp) == 0) + return; + /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the @@ -989,23 +965,8 @@ static void rc_close(struct tty_struct *tty, struct file *filp) rc_shutdown_port(tty, bp, port); rc_flush_buffer(tty); spin_unlock_irqrestore(&riscom_lock, flags); - tty_ldisc_flush(tty); - spin_lock_irqsave(&port->port.lock, flags); - tty->closing = 0; - port->port.tty = NULL; - if (port->port.blocked_open) { - spin_unlock_irqrestore(&port->port.lock, flags); - if (port->port.close_delay) - msleep_interruptible(jiffies_to_msecs(port->port.close_delay)); - wake_up_interruptible(&port->port.open_wait); - spin_lock_irqsave(&port->port.lock, flags); - } - port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&port->port.close_wait); - -out: - spin_unlock_irqrestore(&riscom_lock, flags); + tty_port_close_end(&port->port, tty); } static int rc_write(struct tty_struct *tty, diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 77eef61c46f3..e1e0dd89ac9a 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -833,40 +833,20 @@ static void stl_close(struct tty_struct *tty, struct file *filp) pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp); portp = tty->driver_data; - if (portp == NULL) - return; + BUG_ON(portp == NULL); + port = &portp->port; - spin_lock_irqsave(&port->lock, flags); - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&port->lock, flags); + if (tty_port_close_start(port, tty, filp) == 0) return; - } - if (tty->count == 1 && port->count != 1) - port->count = 1; - if (port->count-- > 1) { - spin_unlock_irqrestore(&port->lock, flags); - return; - } - - port->count = 0; - port->flags |= ASYNC_CLOSING; - /* * May want to wait for any data to drain before closing. The BUSY * flag keeps track of whether we are still sending or not - it is * very accurate for the cd1400, not quite so for the sc26198. * (The sc26198 has no "end-of-data" interrupt only empty FIFO) */ - tty->closing = 1; - - spin_unlock_irqrestore(&port->lock, flags); - - if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, portp->closing_wait); stl_waituntilsent(tty, (HZ / 2)); - spin_lock_irqsave(&port->lock, flags); portp->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&port->lock, flags); @@ -883,20 +863,9 @@ static void stl_close(struct tty_struct *tty, struct file *filp) portp->tx.head = NULL; portp->tx.tail = NULL; } - set_bit(TTY_IO_ERROR, &tty->flags); - tty_ldisc_flush(tty); - tty->closing = 0; + tty_port_close_end(port, tty); tty_port_tty_set(port, NULL); - - if (port->blocked_open) { - if (portp->close_delay) - msleep_interruptible(jiffies_to_msecs(portp->close_delay)); - wake_up_interruptible(&portp->port.open_wait); - } - - portp->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&port->close_wait); } /*****************************************************************************/ diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 0ded4ed3da3c..fbd5a5ce2e1c 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -3104,70 +3104,18 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_close(%s) entry, count=%d\n", __FILE__,__LINE__, info->device_name, info->port.count); - - if (!info->port.count) - return; - if (tty_hung_up_p(filp)) + if (tty_port_close_start(&info->port, tty, filp) == 0) goto cleanup; - if ((tty->count == 1) && (info->port.count != 1)) { - /* - * tty->count is 1 and the tty structure will be freed. - * info->port.count should be one in this case. - * if it's not, correct it so that the port is shutdown. - */ - printk("mgsl_close: bad refcount; tty->count is 1, " - "info->port.count is %d\n", info->port.count); - info->port.count = 1; - } - - info->port.count--; - - /* if at least one open remaining, leave hardware active */ - if (info->port.count) - goto cleanup; - - info->port.flags |= ASYNC_CLOSING; - - /* set tty->closing to notify line discipline to - * only process XON/XOFF characters. Only the N_TTY - * discipline appears to use this (ppp does not). - */ - tty->closing = 1; - - /* wait for transmit data to clear all layers */ - - if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_close(%s) calling tty_wait_until_sent\n", - __FILE__,__LINE__, info->device_name ); - tty_wait_until_sent(tty, info->port.closing_wait); - } - if (info->port.flags & ASYNC_INITIALIZED) mgsl_wait_until_sent(tty, info->timeout); - mgsl_flush_buffer(tty); - tty_ldisc_flush(tty); - shutdown(info); - - tty->closing = 0; + + tty_port_close_end(&info->port, tty); info->port.tty = NULL; - - if (info->port.blocked_open) { - if (info->port.close_delay) { - msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); - } - wake_up_interruptible(&info->port.open_wait); - } - - info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - - wake_up_interruptible(&info->port.close_wait); - cleanup: if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_close(%s) exit, count=%d\n", __FILE__,__LINE__, diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 625c9bde3be8..53544e21f191 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -720,44 +720,9 @@ static void close(struct tty_struct *tty, struct file *filp) return; DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count)); - if (!info->port.count) - return; - - if (tty_hung_up_p(filp)) + if (tty_port_close_start(&info->port, tty, filp) == 0) goto cleanup; - if ((tty->count == 1) && (info->port.count != 1)) { - /* - * tty->count is 1 and the tty structure will be freed. - * info->port.count should be one in this case. - * if it's not, correct it so that the port is shutdown. - */ - DBGERR(("%s close: bad refcount; tty->count=1, " - "info->port.count=%d\n", info->device_name, info->port.count)); - info->port.count = 1; - } - - info->port.count--; - - /* if at least one open remaining, leave hardware active */ - if (info->port.count) - goto cleanup; - - info->port.flags |= ASYNC_CLOSING; - - /* set tty->closing to notify line discipline to - * only process XON/XOFF characters. Only the N_TTY - * discipline appears to use this (ppp does not). - */ - tty->closing = 1; - - /* wait for transmit data to clear all layers */ - - if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { - DBGINFO(("%s call tty_wait_until_sent\n", info->device_name)); - tty_wait_until_sent(tty, info->port.closing_wait); - } - if (info->port.flags & ASYNC_INITIALIZED) wait_until_sent(tty, info->timeout); flush_buffer(tty); @@ -765,20 +730,8 @@ static void close(struct tty_struct *tty, struct file *filp) shutdown(info); - tty->closing = 0; + tty_port_close_end(&info->port, tty); info->port.tty = NULL; - - if (info->port.blocked_open) { - if (info->port.close_delay) { - msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); - } - wake_up_interruptible(&info->port.open_wait); - } - - info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - - wake_up_interruptible(&info->port.close_wait); - cleanup: DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count)); } diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 1f5c21ec4b14..2aac55bcf5fd 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -810,70 +810,18 @@ static void close(struct tty_struct *tty, struct file *filp) printk("%s(%d):%s close() entry, count=%d\n", __FILE__,__LINE__, info->device_name, info->port.count); - if (!info->port.count) - return; - - if (tty_hung_up_p(filp)) - goto cleanup; - - if ((tty->count == 1) && (info->port.count != 1)) { - /* - * tty->count is 1 and the tty structure will be freed. - * info->port.count should be one in this case. - * if it's not, correct it so that the port is shutdown. - */ - printk("%s(%d):%s close: bad refcount; tty->count is 1, " - "info->port.count is %d\n", - __FILE__,__LINE__, info->device_name, info->port.count); - info->port.count = 1; - } - - info->port.count--; - - /* if at least one open remaining, leave hardware active */ - if (info->port.count) + if (tty_port_close_start(&info->port, tty, filp) == 0) goto cleanup; - - info->port.flags |= ASYNC_CLOSING; - - /* set tty->closing to notify line discipline to - * only process XON/XOFF characters. Only the N_TTY - * discipline appears to use this (ppp does not). - */ - tty->closing = 1; - - /* wait for transmit data to clear all layers */ - - if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s close() calling tty_wait_until_sent\n", - __FILE__,__LINE__, info->device_name ); - tty_wait_until_sent(tty, info->port.closing_wait); - } - + if (info->port.flags & ASYNC_INITIALIZED) wait_until_sent(tty, info->timeout); flush_buffer(tty); - tty_ldisc_flush(tty); - shutdown(info); - tty->closing = 0; + tty_port_close_end(&info->port, tty); info->port.tty = NULL; - - if (info->port.blocked_open) { - if (info->port.close_delay) { - msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); - } - wake_up_interruptible(&info->port.open_wait); - } - - info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - - wake_up_interruptible(&info->port.close_wait); - cleanup: if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s close() exit, count=%d\n", __FILE__,__LINE__, diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 0723664fe0ab..b3175f54fe05 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -257,3 +257,61 @@ int tty_port_block_til_ready(struct tty_port *port, } EXPORT_SYMBOL(tty_port_block_til_ready); +int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (tty_hung_up_p(filp)) { + spin_unlock_irqrestore(&port->lock, flags); + return 0; + } + + if( tty->count == 1 && port->count != 1) { + printk(KERN_WARNING + "tty_port_close_start: tty->count = 1 port count = %d.\n", + port->count); + port->count = 1; + } + if (--port->count < 0) { + printk(KERN_WARNING "tty_port_close_start: count = %d\n", + port->count); + port->count = 0; + } + + if (port->count) { + spin_unlock_irqrestore(&port->lock, flags); + return 0; + } + port->flags |= ASYNC_CLOSING; + tty->closing = 1; + spin_unlock_irqrestore(&port->lock, flags); + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->closing_wait); + return 1; +} +EXPORT_SYMBOL(tty_port_close_start); + +void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) +{ + unsigned long flags; + + tty_ldisc_flush(tty); + + spin_lock_irqsave(&port->lock, flags); + tty->closing = 0; + + if (port->blocked_open) { + spin_unlock_irqrestore(&port->lock, flags); + if (port->close_delay) { + msleep_interruptible( + jiffies_to_msecs(port->close_delay)); + } + spin_lock_irqsave(&port->lock, flags); + wake_up_interruptible(&port->open_wait); + } + port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + wake_up_interruptible(&port->close_wait); + spin_unlock_irqrestore(&port->lock, flags); +} +EXPORT_SYMBOL(tty_port_close_end); diff --git a/include/linux/istallion.h b/include/linux/istallion.h index 053d5aea925c..7faca98c7d14 100644 --- a/include/linux/istallion.h +++ b/include/linux/istallion.h @@ -59,7 +59,6 @@ struct stliport { unsigned int devnr; int baud_base; int custom_divisor; - int close_delay; int closing_wait; int rc; int argsize; diff --git a/include/linux/tty.h b/include/linux/tty.h index 61a0ab32cf11..fc39db95499f 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -441,6 +441,9 @@ extern void tty_port_raise_dtr_rts(struct tty_port *port); extern void tty_port_hangup(struct tty_port *port); extern int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty, struct file *filp); +extern int tty_port_close_start(struct tty_port *port, + struct tty_struct *tty, struct file *filp); +extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); -- cgit v1.2.3 From 39aced68d664291db3324d0fcf0985ab5626aac2 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Fri, 2 Jan 2009 13:46:58 +0000 Subject: serial: set correct baud_base for Oxford Semiconductor Ltd EXSYS EX-41092 Dual 16950 Serial adapter The PCI-card identified as "Oxford Semiconductor Ltd EXSYS EX-41092 Dual 16950 Serial adapter" is only usable with other devices (i.e. not the same card) after doing a "setserial /dev/ttyS baud_base 115200". This baud_base should be default for this card. Signed-off-by: Niels de Vos Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/8250_pci.c | 3 +++ include/linux/pci_ids.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include/linux') diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 0b794138f686..2a2e1c717e8e 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -2387,6 +2387,9 @@ static struct pci_device_id serial_pci_tbl[] = { * www.ussg.iu.edu/hypermail/linux/kernel/0303.1/0516.html). * For now just used the hex ID 0x950a. */ + { PCI_VENDOR_ID_OXSEMI, 0x950a, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0, + pbn_b0_2_115200 }, { PCI_VENDOR_ID_OXSEMI, 0x950a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_2_1130000 }, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index b6e694454280..fa83dfefc5e0 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1766,6 +1766,7 @@ #define PCI_DEVICE_ID_SIIG_8S_20x_650 0x2081 #define PCI_DEVICE_ID_SIIG_8S_20x_850 0x2082 #define PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL 0x2050 +#define PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL 0x2530 #define PCI_VENDOR_ID_RADISYS 0x1331 -- cgit v1.2.3 From 60c20fb8c00a2b23308ae4517f145383bc66d291 Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Fri, 2 Jan 2009 13:49:04 +0000 Subject: serial: RS485 ioctl structure uses __u32 include linux/types.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the commit below a new struct serial_rs485 was introduced for a new ioctl: commit c26c56c0f40e200e61d1390629c806f6adaffbcc Author: Alan Cox Date: Mon Oct 13 10:37:48 2008 +0100 tty: Cris has a nice RS485 ioctl so we should steal it This structure uses the __u32 types for some of its members, which leads to the following compile error: $ cc -I.../include -c X.c In file included from X.c:2: .../include/linux/serial.h:185: error: expected specifier-qualifier-list before ‘__u32’ $ It seems that these types are appropriate for this structure as it is to be exposed to userspace. These types are available via linux/types.h so move the include of that outside the __KERNEL__ section. Signed-off-by: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- include/linux/serial.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/serial.h b/include/linux/serial.h index 1ea8d9265bf6..9136cc5608c3 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -10,8 +10,9 @@ #ifndef _LINUX_SERIAL_H #define _LINUX_SERIAL_H -#ifdef __KERNEL__ #include + +#ifdef __KERNEL__ #include /* -- cgit v1.2.3 From f751928e0ddf54ea4fe5546f35e99efc5b5d9938 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 2 Jan 2009 13:49:21 +0000 Subject: tty: We want the port object to be persistent Move the tty_port and uart_info bits around a little. By embedding the uart_info into the uart_port we get rid of lots of corner case testing and also get the ability to go port<->state<->info which is a bit more elegant than the current data structures. Downsides - we allocate a tiny bit more memory for unused ports, upside we've removed as much code as it saved for most users.. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/jsm/jsm_tty.c | 2 +- drivers/serial/serial_core.c | 144 ++++++++++++++++++------------------------- include/linux/serial_core.h | 62 ++++++++++--------- 3 files changed, 96 insertions(+), 112 deletions(-) (limited to 'include/linux') diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c index a697914ae3d0..3547558d2caf 100644 --- a/drivers/serial/jsm/jsm_tty.c +++ b/drivers/serial/jsm/jsm_tty.c @@ -272,7 +272,7 @@ static void jsm_tty_close(struct uart_port *port) jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n"); bd = channel->ch_bd; - ts = channel->uart_port.info->port.tty->termios; + ts = port->info->port.tty->termios; channel->ch_flags &= ~(CH_STOPI); diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 874786a11fe9..daeba1c52c8e 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -50,7 +50,7 @@ static struct lock_class_key port_lock_key; #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) -#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->port.blocked_open : 0)) +#define uart_users(state) ((state)->count + (state)->info.port.blocked_open) #ifdef CONFIG_SERIAL_CORE_CONSOLE #define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) @@ -94,7 +94,7 @@ static void __uart_start(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; - if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && + if (!uart_circ_empty(&state->info.xmit) && state->info.xmit.buf && !tty->stopped && !tty->hw_stopped) port->ops->start_tx(port); } @@ -113,7 +113,7 @@ static void uart_start(struct tty_struct *tty) static void uart_tasklet_action(unsigned long data) { struct uart_state *state = (struct uart_state *)data; - tty_wakeup(state->info->port.tty); + tty_wakeup(state->info.port.tty); } static inline void @@ -139,7 +139,7 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) */ static int uart_startup(struct uart_state *state, int init_hw) { - struct uart_info *info = state->info; + struct uart_info *info = &state->info; struct uart_port *port = state->port; unsigned long page; int retval = 0; @@ -212,14 +212,15 @@ static int uart_startup(struct uart_state *state, int init_hw) */ static void uart_shutdown(struct uart_state *state) { - struct uart_info *info = state->info; + struct uart_info *info = &state->info; struct uart_port *port = state->port; + struct tty_struct *tty = info->port.tty; /* * Set the TTY IO error marker */ - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); if (info->flags & UIF_INITIALIZED) { info->flags &= ~UIF_INITIALIZED; @@ -227,7 +228,7 @@ static void uart_shutdown(struct uart_state *state) /* * Turn off DTR and RTS early. */ - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) + if (!tty || (tty->termios->c_cflag & HUPCL)) uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); /* @@ -427,7 +428,7 @@ EXPORT_SYMBOL(uart_get_divisor); static void uart_change_speed(struct uart_state *state, struct ktermios *old_termios) { - struct tty_struct *tty = state->info->port.tty; + struct tty_struct *tty = state->info.port.tty; struct uart_port *port = state->port; struct ktermios *termios; @@ -444,14 +445,14 @@ uart_change_speed(struct uart_state *state, struct ktermios *old_termios) * Set flags based on termios cflag */ if (termios->c_cflag & CRTSCTS) - state->info->flags |= UIF_CTS_FLOW; + state->info.flags |= UIF_CTS_FLOW; else - state->info->flags &= ~UIF_CTS_FLOW; + state->info.flags &= ~UIF_CTS_FLOW; if (termios->c_cflag & CLOCAL) - state->info->flags &= ~UIF_CHECK_CD; + state->info.flags &= ~UIF_CHECK_CD; else - state->info->flags |= UIF_CHECK_CD; + state->info.flags |= UIF_CHECK_CD; port->ops->set_termios(port, termios, old_termios); } @@ -479,7 +480,7 @@ static int uart_put_char(struct tty_struct *tty, unsigned char ch) { struct uart_state *state = tty->driver_data; - return __uart_put_char(state->port, &state->info->xmit, ch); + return __uart_put_char(state->port, &state->info.xmit, ch); } static void uart_flush_chars(struct tty_struct *tty) @@ -500,13 +501,13 @@ uart_write(struct tty_struct *tty, const unsigned char *buf, int count) * This means you called this function _after_ the port was * closed. No cookie for you. */ - if (!state || !state->info) { + if (!state) { WARN_ON(1); return -EL3HLT; } port = state->port; - circ = &state->info->xmit; + circ = &state->info.xmit; if (!circ->buf) return 0; @@ -537,7 +538,7 @@ static int uart_write_room(struct tty_struct *tty) int ret; spin_lock_irqsave(&state->port->lock, flags); - ret = uart_circ_chars_free(&state->info->xmit); + ret = uart_circ_chars_free(&state->info.xmit); spin_unlock_irqrestore(&state->port->lock, flags); return ret; } @@ -549,7 +550,7 @@ static int uart_chars_in_buffer(struct tty_struct *tty) int ret; spin_lock_irqsave(&state->port->lock, flags); - ret = uart_circ_chars_pending(&state->info->xmit); + ret = uart_circ_chars_pending(&state->info.xmit); spin_unlock_irqrestore(&state->port->lock, flags); return ret; } @@ -564,7 +565,7 @@ static void uart_flush_buffer(struct tty_struct *tty) * This means you called this function _after_ the port was * closed. No cookie for you. */ - if (!state || !state->info) { + if (!state) { WARN_ON(1); return; } @@ -573,7 +574,7 @@ static void uart_flush_buffer(struct tty_struct *tty) pr_debug("uart_flush_buffer(%d) called\n", tty->index); spin_lock_irqsave(&port->lock, flags); - uart_circ_clear(&state->info->xmit); + uart_circ_clear(&state->info.xmit); if (port->ops->flush_buffer) port->ops->flush_buffer(port); spin_unlock_irqrestore(&port->lock, flags); @@ -837,15 +838,15 @@ static int uart_set_info(struct uart_state *state, state->closing_wait = closing_wait; if (new_serial.xmit_fifo_size) port->fifosize = new_serial.xmit_fifo_size; - if (state->info->port.tty) - state->info->port.tty->low_latency = + if (state->info.port.tty) + state->info.port.tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: retval = 0; if (port->type == PORT_UNKNOWN) goto exit; - if (state->info->flags & UIF_INITIALIZED) { + if (state->info.flags & UIF_INITIALIZED) { if (((old_flags ^ port->flags) & UPF_SPD_MASK) || old_custom_divisor != port->custom_divisor) { /* @@ -858,7 +859,7 @@ static int uart_set_info(struct uart_state *state, printk(KERN_NOTICE "%s sets custom speed on %s. This " "is deprecated.\n", current->comm, - tty_name(state->info->port.tty, buf)); + tty_name(state->info.port.tty, buf)); } uart_change_speed(state, NULL); } @@ -889,8 +890,8 @@ static int uart_get_lsr_info(struct uart_state *state, * interrupt happens). */ if (port->x_char || - ((uart_circ_chars_pending(&state->info->xmit) > 0) && - !state->info->port.tty->stopped && !state->info->port.tty->hw_stopped)) + ((uart_circ_chars_pending(&state->info.xmit) > 0) && + !state->info.port.tty->stopped && !state->info.port.tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value); @@ -1017,7 +1018,7 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) port->ops->enable_ms(port); spin_unlock_irq(&port->lock); - add_wait_queue(&state->info->delta_msr_wait, &wait); + add_wait_queue(&state->info.delta_msr_wait, &wait); for (;;) { spin_lock_irq(&port->lock); memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); @@ -1045,7 +1046,7 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) } current->state = TASK_RUNNING; - remove_wait_queue(&state->info->delta_msr_wait, &wait); + remove_wait_queue(&state->info.delta_msr_wait, &wait); return ret; } @@ -1241,7 +1242,7 @@ static void uart_set_termios(struct tty_struct *tty, */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&state->info->port.open_wait); + wake_up_interruptible(&info->port.open_wait); #endif } @@ -1303,7 +1304,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ - if (state->info->flags & UIF_INITIALIZED) { + if (state->info.flags & UIF_INITIALIZED) { unsigned long flags; spin_lock_irqsave(&port->lock, flags); port->ops->stop_rx(port); @@ -1322,9 +1323,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp) tty_ldisc_flush(tty); tty->closing = 0; - state->info->port.tty = NULL; + state->info.port.tty = NULL; - if (state->info->port.blocked_open) { + if (state->info.port.blocked_open) { if (state->close_delay) msleep_interruptible(state->close_delay); } else if (!uart_console(port)) { @@ -1334,8 +1335,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp) /* * Wake up anyone trying to open this port. */ - state->info->flags &= ~UIF_NORMAL_ACTIVE; - wake_up_interruptible(&state->info->port.open_wait); + state->info.flags &= ~UIF_NORMAL_ACTIVE; + wake_up_interruptible(&state->info.port.open_wait); done: mutex_unlock(&state->mutex); @@ -1409,19 +1410,20 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) static void uart_hangup(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; + struct uart_info *info = &state->info; BUG_ON(!kernel_locked()); pr_debug("uart_hangup(%d)\n", state->port->line); mutex_lock(&state->mutex); - if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { + if (info->flags & UIF_NORMAL_ACTIVE) { uart_flush_buffer(tty); uart_shutdown(state); state->count = 0; - state->info->flags &= ~UIF_NORMAL_ACTIVE; - state->info->port.tty = NULL; - wake_up_interruptible(&state->info->port.open_wait); - wake_up_interruptible(&state->info->delta_msr_wait); + info->flags &= ~UIF_NORMAL_ACTIVE; + info->port.tty = NULL; + wake_up_interruptible(&info->port.open_wait); + wake_up_interruptible(&info->delta_msr_wait); } mutex_unlock(&state->mutex); } @@ -1434,7 +1436,7 @@ static void uart_hangup(struct tty_struct *tty) */ static void uart_update_termios(struct uart_state *state) { - struct tty_struct *tty = state->info->port.tty; + struct tty_struct *tty = state->info.port.tty; struct uart_port *port = state->port; if (uart_console(port) && port->cons->cflag) { @@ -1469,7 +1471,7 @@ static int uart_block_til_ready(struct file *filp, struct uart_state *state) { DECLARE_WAITQUEUE(wait, current); - struct uart_info *info = state->info; + struct uart_info *info = &state->info; struct uart_port *port = state->port; unsigned int mctrl; @@ -1563,28 +1565,6 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) ret = -ENXIO; goto err_unlock; } - - /* BKL: RACE HERE - LEAK */ - /* We should move this into the uart_state structure and kill off - this whole complexity */ - if (!state->info) { - state->info = kzalloc(sizeof(struct uart_info), GFP_KERNEL); - if (state->info) { - init_waitqueue_head(&state->info->port.open_wait); - init_waitqueue_head(&state->info->delta_msr_wait); - - /* - * Link the info into the other structures. - */ - state->port->info = state->info; - - tasklet_init(&state->info->tlet, uart_tasklet_action, - (unsigned long)state); - } else { - ret = -ENOMEM; - goto err_unlock; - } - } return state; err_unlock: @@ -1641,9 +1621,10 @@ static int uart_open(struct tty_struct *tty, struct file *filp) * Any failures from here onwards should not touch the count. */ tty->driver_data = state; + state->port->info = &state->info; tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0; - state->info->port.tty = tty; + state->info.port.tty = tty; /* * If the port is in the middle of closing, bail out now. @@ -1676,8 +1657,8 @@ static int uart_open(struct tty_struct *tty, struct file *filp) /* * If this is the first open to succeed, adjust things to suit. */ - if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { - state->info->flags |= UIF_NORMAL_ACTIVE; + if (retval == 0 && !(state->info.flags & UIF_NORMAL_ACTIVE)) { + state->info.flags |= UIF_NORMAL_ACTIVE; uart_update_termios(state); } @@ -2028,11 +2009,11 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port) } port->suspended = 1; - if (state->info && state->info->flags & UIF_INITIALIZED) { + if (state->info.flags & UIF_INITIALIZED) { const struct uart_ops *ops = port->ops; int tries; - state->info->flags = (state->info->flags & ~UIF_INITIALIZED) + state->info.flags = (state->info.flags & ~UIF_INITIALIZED) | UIF_SUSPENDED; spin_lock_irq(&port->lock); @@ -2107,15 +2088,15 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port) /* * If that's unset, use the tty termios setting. */ - if (state->info && state->info->port.tty && termios.c_cflag == 0) - termios = *state->info->port.tty->termios; + if (state->info.port.tty && termios.c_cflag == 0) + termios = *state->info.port.tty->termios; uart_change_pm(state, 0); port->ops->set_termios(port, &termios, NULL); console_start(port->cons); } - if (state->info && state->info->flags & UIF_SUSPENDED) { + if (state->info.flags & UIF_SUSPENDED) { const struct uart_ops *ops = port->ops; int ret; @@ -2130,7 +2111,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port) ops->set_mctrl(port, port->mctrl); ops->start_tx(port); spin_unlock_irq(&port->lock); - state->info->flags |= UIF_INITIALIZED; + state->info.flags |= UIF_INITIALIZED; } else { /* * Failed to resume - maybe hardware went away? @@ -2140,7 +2121,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port) uart_shutdown(state); } - state->info->flags &= ~UIF_SUSPENDED; + state->info.flags &= ~UIF_SUSPENDED; } mutex_unlock(&state->mutex); @@ -2383,8 +2364,12 @@ int uart_register_driver(struct uart_driver *drv) state->close_delay = 500; /* .5 seconds */ state->closing_wait = 30000; /* 30 seconds */ - mutex_init(&state->mutex); + + tty_port_init(&state->info.port); + init_waitqueue_head(&state->info.delta_msr_wait); + tasklet_init(&state->info.tlet, uart_tasklet_action, + (unsigned long)state); } retval = tty_register_driver(normal); @@ -2455,7 +2440,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) state->pm_state = -1; port->cons = drv->cons; - port->info = state->info; + port->info = &state->info; /* * If this port is a console, then the spinlock is already @@ -2527,17 +2512,10 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) */ tty_unregister_device(drv->tty_driver, port->line); - info = state->info; + info = &state->info; if (info && info->port.tty) tty_vhangup(info->port.tty); - /* - * All users of this port should now be disconnected from - * this driver, and the port shut down. We should be the - * only thread fiddling with this port from now on. - */ - state->info = NULL; - /* * Free the port IO and memory resources, if any. */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index feb3b939ec4b..2395969faa04 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -315,36 +315,14 @@ struct uart_port { void *private_data; /* generic platform data pointer */ }; -/* - * This is the state information which is persistent across opens. - * The low level driver must not to touch any elements contained - * within. - */ -struct uart_state { - unsigned int close_delay; /* msec */ - unsigned int closing_wait; /* msec */ - -#define USF_CLOSING_WAIT_INF (0) -#define USF_CLOSING_WAIT_NONE (~0U) - - int count; - int pm_state; - struct uart_info *info; - struct uart_port *port; - - struct mutex mutex; -}; - -#define UART_XMIT_SIZE PAGE_SIZE - -typedef unsigned int __bitwise__ uif_t; - /* * This is the state information which is only valid when the port - * is open; it may be freed by the core driver once the device has + * is open; it may be cleared the core driver once the device has * been closed. Either the low level driver or the core can modify * stuff here. */ +typedef unsigned int __bitwise__ uif_t; + struct uart_info { struct tty_port port; struct circ_buf xmit; @@ -366,6 +344,29 @@ struct uart_info { wait_queue_head_t delta_msr_wait; }; +/* + * This is the state information which is persistent across opens. + * The low level driver must not to touch any elements contained + * within. + */ +struct uart_state { + unsigned int close_delay; /* msec */ + unsigned int closing_wait; /* msec */ + +#define USF_CLOSING_WAIT_INF (0) +#define USF_CLOSING_WAIT_NONE (~0U) + + int count; + int pm_state; + struct uart_info info; + struct uart_port *port; + + struct mutex mutex; +}; + +#define UART_XMIT_SIZE PAGE_SIZE + + /* number of characters left in xmit buffer before we ask for more */ #define WAKEUP_CHARS 256 @@ -439,8 +440,13 @@ int uart_resume_port(struct uart_driver *reg, struct uart_port *port); #define uart_circ_chars_free(circ) \ (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) -#define uart_tx_stopped(portp) \ - ((portp)->info->port.tty->stopped || (portp)->info->port.tty->hw_stopped) +static inline int uart_tx_stopped(struct uart_port *port) +{ + struct tty_struct *tty = port->info->port.tty; + if(tty->stopped || tty->hw_stopped) + return 1; + return 0; +} /* * The following are helper functions for the low level drivers. @@ -451,7 +457,7 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) #ifdef SUPPORT_SYSRQ if (port->sysrq) { if (ch && time_before(jiffies, port->sysrq)) { - handle_sysrq(ch, port->info ? port->info->port.tty : NULL); + handle_sysrq(ch, port->info->port.tty); port->sysrq = 0; return 1; } -- cgit v1.2.3 From 7d6a07d123b62bf4fa71867420c23da3ca36c995 Mon Sep 17 00:00:00 2001 From: David Daney Date: Fri, 2 Jan 2009 13:49:47 +0000 Subject: 8250: Serial driver changes to support future Cavium OCTEON serial patches. In order to use Cavium OCTEON specific serial i/o drivers, we first patch the 8250 driver to use replaceable I/O functions. Compatible I/O functions are added for existing iotypeS. An added benefit of this change is that it makes it easy to factor some of the existing special cases out to board/SOC specific support code. The alternative is to load up 8250.c with a bunch of OCTEON specific iotype code and bug work-arounds. Signed-off-by: David Daney Signed-off-by: Tomaso Paoletti Signed-off-by: Andrew Morton Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/8250.c | 194 +++++++++++++++++++++++++++++++------------- include/linux/serial_8250.h | 2 + include/linux/serial_core.h | 2 + 3 files changed, 140 insertions(+), 58 deletions(-) (limited to 'include/linux') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 8e28750a4058..849af9d21feb 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -303,16 +303,16 @@ static const u8 au_io_out_map[] = { }; /* sane hardware needs no mapping */ -static inline int map_8250_in_reg(struct uart_8250_port *up, int offset) +static inline int map_8250_in_reg(struct uart_port *p, int offset) { - if (up->port.iotype != UPIO_AU) + if (p->iotype != UPIO_AU) return offset; return au_io_in_map[offset]; } -static inline int map_8250_out_reg(struct uart_8250_port *up, int offset) +static inline int map_8250_out_reg(struct uart_port *p, int offset) { - if (up->port.iotype != UPIO_AU) + if (p->iotype != UPIO_AU) return offset; return au_io_out_map[offset]; } @@ -341,16 +341,16 @@ static const u8 [UART_SCR] = 0x2c }; -static inline int map_8250_in_reg(struct uart_8250_port *up, int offset) +static inline int map_8250_in_reg(struct uart_port *p, int offset) { - if (up->port.iotype != UPIO_RM9000) + if (p->iotype != UPIO_RM9000) return offset; return regmap_in[offset]; } -static inline int map_8250_out_reg(struct uart_8250_port *up, int offset) +static inline int map_8250_out_reg(struct uart_port *p, int offset) { - if (up->port.iotype != UPIO_RM9000) + if (p->iotype != UPIO_RM9000) return offset; return regmap_out[offset]; } @@ -363,108 +363,170 @@ static inline int map_8250_out_reg(struct uart_8250_port *up, int offset) #endif -static unsigned int serial_in(struct uart_8250_port *up, int offset) +static unsigned int hub6_serial_in(struct uart_port *p, int offset) { - unsigned int tmp; - offset = map_8250_in_reg(up, offset) << up->port.regshift; + offset = map_8250_in_reg(p, offset) << p->regshift; + outb(p->hub6 - 1 + offset, p->iobase); + return inb(p->iobase + 1); +} - switch (up->port.iotype) { - case UPIO_HUB6: - outb(up->port.hub6 - 1 + offset, up->port.iobase); - return inb(up->port.iobase + 1); +static void hub6_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + outb(p->hub6 - 1 + offset, p->iobase); + outb(value, p->iobase + 1); +} - case UPIO_MEM: - case UPIO_DWAPB: - return readb(up->port.membase + offset); +static unsigned int mem_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return readb(p->membase + offset); +} - case UPIO_RM9000: - case UPIO_MEM32: - return readl(up->port.membase + offset); +static void mem_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + writeb(value, p->membase + offset); +} + +static void mem32_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + writel(value, p->membase + offset); +} + +static unsigned int mem32_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return readl(p->membase + offset); +} #ifdef CONFIG_SERIAL_8250_AU1X00 - case UPIO_AU: - return __raw_readl(up->port.membase + offset); +static unsigned int au_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return __raw_readl(p->membase + offset); +} + +static void au_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + __raw_writel(value, p->membase + offset); +} #endif - case UPIO_TSI: - if (offset == UART_IIR) { - tmp = readl(up->port.membase + (UART_IIR & ~3)); - return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */ - } else - return readb(up->port.membase + offset); +static unsigned int tsi_serial_in(struct uart_port *p, int offset) +{ + unsigned int tmp; + offset = map_8250_in_reg(p, offset) << p->regshift; + if (offset == UART_IIR) { + tmp = readl(p->membase + (UART_IIR & ~3)); + return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */ + } else + return readb(p->membase + offset); +} - default: - return inb(up->port.iobase + offset); - } +static void tsi_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + if (!((offset == UART_IER) && (value & UART_IER_UUE))) + writeb(value, p->membase + offset); } -static void -serial_out(struct uart_8250_port *up, int offset, int value) +static void dwapb_serial_out(struct uart_port *p, int offset, int value) { - /* Save the offset before it's remapped */ int save_offset = offset; - offset = map_8250_out_reg(up, offset) << up->port.regshift; + offset = map_8250_out_reg(p, offset) << p->regshift; + /* Save the LCR value so it can be re-written when a + * Busy Detect interrupt occurs. */ + if (save_offset == UART_LCR) { + struct uart_8250_port *up = (struct uart_8250_port *)p; + up->lcr = value; + } + writeb(value, p->membase + offset); + /* Read the IER to ensure any interrupt is cleared before + * returning from ISR. */ + if (save_offset == UART_TX || save_offset == UART_IER) + value = p->serial_in(p, UART_IER); +} - switch (up->port.iotype) { +static unsigned int io_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return inb(p->iobase + offset); +} + +static void io_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + outb(value, p->iobase + offset); +} + +static void set_io_from_upio(struct uart_port *p) +{ + switch (p->iotype) { case UPIO_HUB6: - outb(up->port.hub6 - 1 + offset, up->port.iobase); - outb(value, up->port.iobase + 1); + p->serial_in = hub6_serial_in; + p->serial_out = hub6_serial_out; break; case UPIO_MEM: - writeb(value, up->port.membase + offset); + p->serial_in = mem_serial_in; + p->serial_out = mem_serial_out; break; case UPIO_RM9000: case UPIO_MEM32: - writel(value, up->port.membase + offset); + p->serial_in = mem32_serial_in; + p->serial_out = mem32_serial_out; break; #ifdef CONFIG_SERIAL_8250_AU1X00 case UPIO_AU: - __raw_writel(value, up->port.membase + offset); + p->serial_in = au_serial_in; + p->serial_out = au_serial_out; break; #endif case UPIO_TSI: - if (!((offset == UART_IER) && (value & UART_IER_UUE))) - writeb(value, up->port.membase + offset); + p->serial_in = tsi_serial_in; + p->serial_out = tsi_serial_out; break; case UPIO_DWAPB: - /* Save the LCR value so it can be re-written when a - * Busy Detect interrupt occurs. */ - if (save_offset == UART_LCR) - up->lcr = value; - writeb(value, up->port.membase + offset); - /* Read the IER to ensure any interrupt is cleared before - * returning from ISR. */ - if (save_offset == UART_TX || save_offset == UART_IER) - value = serial_in(up, UART_IER); + p->serial_in = mem_serial_in; + p->serial_out = dwapb_serial_out; break; default: - outb(value, up->port.iobase + offset); + p->serial_in = io_serial_in; + p->serial_out = io_serial_out; + break; } } static void serial_out_sync(struct uart_8250_port *up, int offset, int value) { - switch (up->port.iotype) { + struct uart_port *p = &up->port; + switch (p->iotype) { case UPIO_MEM: case UPIO_MEM32: #ifdef CONFIG_SERIAL_8250_AU1X00 case UPIO_AU: #endif case UPIO_DWAPB: - serial_out(up, offset, value); - serial_in(up, UART_LCR); /* safe, no side-effects */ + p->serial_out(p, offset, value); + p->serial_in(p, UART_LCR); /* safe, no side-effects */ break; default: - serial_out(up, offset, value); + p->serial_out(p, offset, value); } } +#define serial_in(up, offset) \ + (up->port.serial_in(&(up)->port, (offset))) +#define serial_out(up, offset, value) \ + (up->port.serial_out(&(up)->port, (offset), (value))) /* * We used to support using pause I/O for certain machines. We * haven't supported this for a while, but just in case it's badly @@ -2576,6 +2638,7 @@ static void __init serial8250_isa_init_ports(void) up->port.membase = old_serial_port[i].iomem_base; up->port.iotype = old_serial_port[i].io_type; up->port.regshift = old_serial_port[i].iomem_reg_shift; + set_io_from_upio(&up->port); if (share_irqs) up->port.flags |= UPF_SHARE_IRQ; } @@ -2769,6 +2832,13 @@ int __init early_serial_setup(struct uart_port *port) p->flags = port->flags; p->mapbase = port->mapbase; p->private_data = port->private_data; + + set_io_from_upio(p); + if (port->serial_in) + p->serial_in = port->serial_in; + if (port->serial_out) + p->serial_out = port->serial_out; + return 0; } @@ -2833,6 +2903,8 @@ static int __devinit serial8250_probe(struct platform_device *dev) port.mapbase = p->mapbase; port.hub6 = p->hub6; port.private_data = p->private_data; + port.serial_in = p->serial_in; + port.serial_out = p->serial_out; port.dev = &dev->dev; if (share_irqs) port.flags |= UPF_SHARE_IRQ; @@ -2986,6 +3058,12 @@ int serial8250_register_port(struct uart_port *port) uart->port.private_data = port->private_data; if (port->dev) uart->port.dev = port->dev; + set_io_from_upio(&uart->port); + /* Possibly override default I/O functions. */ + if (port->serial_in) + uart->port.serial_in = port->serial_in; + if (port->serial_out) + uart->port.serial_out = port->serial_out; ret = uart_add_one_port(&serial8250_reg, &uart->port); if (ret == 0) diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 3d37c94abbc8..77d83d929f2c 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -28,6 +28,8 @@ struct plat_serial8250_port { unsigned char iotype; /* UPIO_* */ unsigned char hub6; upf_t flags; /* UPF_* flags */ + unsigned int (*serial_in)(struct uart_port *, int); + void (*serial_out)(struct uart_port *, int, int); }; /* diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 2395969faa04..60061f44f3d8 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -248,6 +248,8 @@ struct uart_port { spinlock_t lock; /* port lock */ unsigned long iobase; /* in/out[bwl] */ unsigned char __iomem *membase; /* read/write[bwl] */ + unsigned int (*serial_in)(struct uart_port *, int); + void (*serial_out)(struct uart_port *, int, int); unsigned int irq; /* irq number */ unsigned int uartclk; /* base uart clock */ unsigned int fifosize; /* tx fifo size */ -- cgit v1.2.3 From 8e23fcc89c8091790903927449f8efb9b4e23960 Mon Sep 17 00:00:00 2001 From: David Daney Date: Fri, 2 Jan 2009 13:49:54 +0000 Subject: Serial: Allow port type to be specified when calling serial8250_register_port. Add flag value UPF_FIXED_TYPE which specifies that the UART type is known and should not be probed. For this case the UARTs properties are just copied out of the uart_config entry. This allows us to keep SOC specific 8250 probe code out of 8250.c. In this case we know the serial hardware will not be changing as it is on the same silicon as the CPU, and we can specify it with certainty in the board/cpu setup code. The alternative is to load up 8250.c with a bunch of OCTEON specific special cases in the probing code. Signed-off-by: David Daney Signed-off-by: Andrew Morton Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/8250.c | 9 +++++++++ drivers/serial/serial_core.c | 7 +++++-- include/linux/serial_8250.h | 1 + include/linux/serial_core.h | 2 ++ 4 files changed, 17 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 849af9d21feb..3ae497422db5 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -2903,6 +2903,7 @@ static int __devinit serial8250_probe(struct platform_device *dev) port.mapbase = p->mapbase; port.hub6 = p->hub6; port.private_data = p->private_data; + port.type = p->type; port.serial_in = p->serial_in; port.serial_out = p->serial_out; port.dev = &dev->dev; @@ -3058,6 +3059,14 @@ int serial8250_register_port(struct uart_port *port) uart->port.private_data = port->private_data; if (port->dev) uart->port.dev = port->dev; + + if (port->flags & UPF_FIXED_TYPE) { + uart->port.type = port->type; + uart->port.fifosize = uart_config[port->type].fifo_size; + uart->capabilities = uart_config[port->type].flags; + uart->tx_loadsz = uart_config[port->type].tx_loadsz; + } + set_io_from_upio(&uart->port); /* Possibly override default I/O functions. */ if (port->serial_in) diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 9425ed69e0f7..dc68b7e0c930 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -2179,11 +2179,14 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, * Now do the auto configuration stuff. Note that config_port * is expected to claim the resources and map the port for us. */ - flags = UART_CONFIG_TYPE; + flags = 0; if (port->flags & UPF_AUTO_IRQ) flags |= UART_CONFIG_IRQ; if (port->flags & UPF_BOOT_AUTOCONF) { - port->type = PORT_UNKNOWN; + if (!(port->flags & UPF_FIXED_TYPE)) { + port->type = PORT_UNKNOWN; + flags |= UART_CONFIG_TYPE; + } port->ops->config_port(port, flags); } diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 77d83d929f2c..d4d2a78ad43e 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -28,6 +28,7 @@ struct plat_serial8250_port { unsigned char iotype; /* UPIO_* */ unsigned char hub6; upf_t flags; /* UPF_* flags */ + unsigned int type; /* If UPF_FIXED_TYPE */ unsigned int (*serial_in)(struct uart_port *, int); void (*serial_out)(struct uart_port *, int, int); }; diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 60061f44f3d8..f155252f148c 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -295,6 +295,8 @@ struct uart_port { #define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16)) #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) +/* The exact UART type is known and should not be probed. */ +#define UPF_FIXED_TYPE ((__force upf_t) (1 << 27)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) #define UPF_FIXED_PORT ((__force upf_t) (1 << 29)) #define UPF_DEAD ((__force upf_t) (1 << 30)) -- cgit v1.2.3 From 6b06f19151c335ee0c5b61839fa4e6838182ebb8 Mon Sep 17 00:00:00 2001 From: David Daney Date: Fri, 2 Jan 2009 13:50:00 +0000 Subject: Serial: UART driver changes for Cavium OCTEON. Cavium UART implementation is not covered by existing uart_configS. Define a new uart_config (PORT_OCTEON) which is specified by OCTEON platform device registration code. Signed-off-by: Tomaso Paoletti Signed-off-by: David Daney Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/8250.c | 7 +++++++ include/linux/serial_core.h | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 3ae497422db5..daa00567bc44 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -279,6 +279,13 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO, }, + [PORT_OCTEON] = { + .name = "OCTEON", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO, + }, }; #if defined (CONFIG_SERIAL_8250_AU1X00) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index f155252f148c..b4199841f1fc 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -40,7 +40,8 @@ #define PORT_NS16550A 14 #define PORT_XSCALE 15 #define PORT_RM9000 16 /* PMC-Sierra RM9xxx internal UART */ -#define PORT_MAX_8250 16 /* max port ID */ +#define PORT_OCTEON 17 /* Cavium OCTEON internal UART */ +#define PORT_MAX_8250 17 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed -- cgit v1.2.3 From e65f0f8271b1b0452334e5da37fd35413a000de4 Mon Sep 17 00:00:00 2001 From: Flavio Leitner Date: Fri, 2 Jan 2009 13:50:43 +0000 Subject: serial_8250: support for Sealevel Systems Model 7803 COMM+8 Add support for Sealevel Systems Model 7803 COMM+8 Signed-off-by: Flavio Leitner Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/8250_pci.c | 3 +++ include/linux/pci_ids.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include/linux') diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 2a2e1c717e8e..c088146b7513 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -2287,6 +2287,9 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_8_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_460800 }, { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_8_115200 }, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index fa83dfefc5e0..218c73b1e6d4 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1796,6 +1796,7 @@ #define PCI_DEVICE_ID_SEALEVEL_UCOMM232 0x7202 #define PCI_DEVICE_ID_SEALEVEL_COMM4 0x7401 #define PCI_DEVICE_ID_SEALEVEL_COMM8 0x7801 +#define PCI_DEVICE_ID_SEALEVEL_7803 0x7803 #define PCI_DEVICE_ID_SEALEVEL_UCOMM8 0x7804 #define PCI_VENDOR_ID_HYPERCOPE 0x1365 -- cgit v1.2.3 From aecde8b53b8ee1330a5a8206200f0d6b8845a6e0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 30 Dec 2008 07:14:19 -0300 Subject: V4L/DVB (10141): v4l2: debugging API changed to match against driver name instead of ID. Since the i2c driver ID will be removed in the near future we have to modify the v4l2 debugging API to use the driver name instead of driver ID. Note that this API is not used in applications other than v4l2-dbg.cpp as it is for debugging and testing only. Should anyone use the old VIDIOC_G_CHIP_IDENT, then this will be logged with a warning that it is deprecated and will be removed in 2.6.30. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 2 +- drivers/media/video/bt8xx/bttv-driver.c | 9 +++-- drivers/media/video/cafe_ccic.c | 7 ++-- drivers/media/video/cs5345.c | 13 +++---- drivers/media/video/cs53l32a.c | 2 +- drivers/media/video/cx18/cx18-i2c.c | 28 +------------- drivers/media/video/cx18/cx18-i2c.h | 1 - drivers/media/video/cx18/cx18-ioctl.c | 41 ++++++++------------ drivers/media/video/cx23885/cx23885-video.c | 8 ++-- drivers/media/video/cx25840/cx25840-core.c | 13 +++---- drivers/media/video/cx88/cx88-video.c | 13 ++++--- drivers/media/video/em28xx/em28xx-video.c | 28 +++++++------- drivers/media/video/ivtv/ivtv-driver.c | 7 ++-- drivers/media/video/ivtv/ivtv-ioctl.c | 21 +++++----- drivers/media/video/m52790.c | 13 +++---- drivers/media/video/msp3400-driver.c | 2 +- drivers/media/video/mt9m001.c | 19 ++++----- drivers/media/video/mt9m111.c | 19 ++++----- drivers/media/video/mt9t031.c | 18 ++++----- drivers/media/video/mt9v022.c | 19 ++++----- drivers/media/video/ov7670.c | 2 +- drivers/media/video/ov772x.c | 7 ++-- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 11 +++--- drivers/media/video/pvrusb2/pvrusb2-hdw.h | 4 +- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 6 +-- drivers/media/video/saa7115.c | 13 +++---- drivers/media/video/saa7127.c | 13 +++---- drivers/media/video/saa7134/saa6752hs.c | 2 +- drivers/media/video/saa7134/saa7134-empress.c | 14 +++---- drivers/media/video/saa7134/saa7134-video.c | 9 +++-- drivers/media/video/saa717x.c | 9 +++-- drivers/media/video/soc_camera.c | 6 +-- drivers/media/video/tvaudio.c | 2 +- drivers/media/video/tvp5150.c | 13 +++---- drivers/media/video/tw9910.c | 6 +-- drivers/media/video/upd64031a.c | 13 +++---- drivers/media/video/upd64083.c | 13 +++---- drivers/media/video/usbvision/usbvision-video.c | 9 +++-- drivers/media/video/v4l2-common.c | 29 +++++++++----- drivers/media/video/v4l2-compat-ioctl32.c | 3 +- drivers/media/video/v4l2-ioctl.c | 15 +++++--- drivers/media/video/v4l2-subdev.c | 2 +- drivers/media/video/vp27smpx.c | 2 +- drivers/media/video/wm8739.c | 2 +- drivers/media/video/wm8775.c | 2 +- include/linux/videodev2.h | 51 ++++++++++++++++++------- include/media/soc_camera.h | 6 +-- include/media/v4l2-chip-ident.h | 4 +- include/media/v4l2-common.h | 6 +-- include/media/v4l2-int-device.h | 2 +- include/media/v4l2-ioctl.h | 6 +-- include/media/v4l2-subdev.h | 6 +-- 52 files changed, 290 insertions(+), 281 deletions(-) (limited to 'include/linux') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 3b483c1e0124..ff124374e9ba 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -184,7 +184,7 @@ may be NULL if the subdev driver does not support anything from that category. It looks like this: struct v4l2_subdev_core_ops { - int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip); + int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip); int (*log_status)(struct v4l2_subdev *sd); int (*init)(struct v4l2_subdev *sd, u32 val); ... diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index ebcb8e5e9c4d..d2f43bd2f841 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -2039,7 +2039,7 @@ static int bttv_log_status(struct file *file, void *f) #ifdef CONFIG_VIDEO_ADV_DEBUG static int bttv_g_register(struct file *file, void *f, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; @@ -2047,18 +2047,19 @@ static int bttv_g_register(struct file *file, void *f, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; /* bt848 has a 12-bit register space */ reg->reg &= 0xfff; reg->val = btread(reg->reg); + reg->size = 1; return 0; } static int bttv_s_register(struct file *file, void *f, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; @@ -2066,7 +2067,7 @@ static int bttv_s_register(struct file *file, void *f, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; /* bt848 has a 12-bit register space */ diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 476171cf5001..34a39d2e4703 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -859,7 +859,7 @@ static int __cafe_cam_reset(struct cafe_camera *cam) */ static int cafe_cam_init(struct cafe_camera *cam) { - struct v4l2_chip_ident chip = { V4L2_CHIP_MATCH_I2C_ADDR, 0, 0, 0 }; + struct v4l2_dbg_chip_ident chip; int ret; mutex_lock(&cam->s_mutex); @@ -869,8 +869,9 @@ static int cafe_cam_init(struct cafe_camera *cam) ret = __cafe_cam_reset(cam); if (ret) goto out; - chip.match_chip = cam->sensor->addr; - ret = __cafe_cam_cmd(cam, VIDIOC_G_CHIP_IDENT, &chip); + chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR; + chip.match.addr = cam->sensor->addr; + ret = __cafe_cam_cmd(cam, VIDIOC_DBG_G_CHIP_IDENT, &chip); if (ret) goto out; cam->sensor_type = chip.ident; diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c index 70fcd0d5de13..14bebf8a116f 100644 --- a/drivers/media/video/cs5345.c +++ b/drivers/media/video/cs5345.c @@ -95,25 +95,24 @@ static int cs5345_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; + reg->size = 1; reg->val = cs5345_read(sd, reg->reg & 0x1f); return 0; } -static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -122,7 +121,7 @@ static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) } #endif -static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index cb65d519cf78..7292a6316e63 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -102,7 +102,7 @@ static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return 0; } -static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 8941f58bed7f..83e1c6333126 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -242,7 +242,7 @@ int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg) return retval; } } - if (cmd != VIDIOC_G_CHIP_IDENT) + if (cmd != VIDIOC_DBG_G_CHIP_IDENT) CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n", addr, cmd); return -ENODEV; @@ -268,17 +268,6 @@ static int cx18_i2c_id_addr(struct cx18 *cx, u32 id) return retval; } -/* Find the i2c device name matching the DRIVERID */ -static const char *cx18_i2c_id_name(u32 id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (hw_driverids[i] == id) - return hw_devicenames[i]; - return "unknown device"; -} - /* Find the i2c device name matching the CX18_HW_ flag */ static const char *cx18_i2c_hw_name(u32 hw) { @@ -326,21 +315,6 @@ int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg) return cx18_call_i2c_client(cx, addr, cmd, arg); } -/* Calls i2c device based on I2C driver ID. */ -int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg) -{ - int addr; - - addr = cx18_i2c_id_addr(cx, id); - if (addr < 0) { - if (cmd != VIDIOC_G_CHIP_IDENT) - CX18_ERR("i2c ID 0x%08x (%s) not found for cmd 0x%x!\n", - id, cx18_i2c_id_name(id), cmd); - return addr; - } - return cx18_call_i2c_client(cx, addr, cmd, arg); -} - /* broadcast cmd for all I2C clients and for the gpio subsystem */ void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg) { diff --git a/drivers/media/video/cx18/cx18-i2c.h b/drivers/media/video/cx18/cx18-i2c.h index 113c3f9a2cc0..4869739013bd 100644 --- a/drivers/media/video/cx18/cx18-i2c.h +++ b/drivers/media/video/cx18/cx18-i2c.h @@ -23,7 +23,6 @@ int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw); int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg); -int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg); int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg); void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg); int cx18_i2c_register(struct cx18 *cx, unsigned idx); diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 8aa152b39545..7086aaba77d6 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -254,30 +254,24 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, } static int cx18_g_chip_ident(struct file *file, void *fh, - struct v4l2_chip_ident *chip) + struct v4l2_dbg_chip_ident *chip) { struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; chip->ident = V4L2_IDENT_NONE; chip->revision = 0; - if (chip->match_type == V4L2_CHIP_MATCH_HOST) { - if (v4l2_chip_match_host(chip->match_type, chip->match_chip)) - chip->ident = V4L2_IDENT_CX23418; + if (v4l2_chip_match_host(&chip->match)) { + chip->ident = V4L2_IDENT_CX23418; return 0; } - if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return cx18_i2c_id(cx, chip->match_chip, VIDIOC_G_CHIP_IDENT, - chip); - if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR) - return cx18_call_i2c_client(cx, chip->match_chip, - VIDIOC_G_CHIP_IDENT, chip); - return -EINVAL; + cx18_call_i2c_clients(cx, VIDIOC_DBG_G_CHIP_IDENT, chip); + return 0; } #ifdef CONFIG_VIDEO_ADV_DEBUG static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) { - struct v4l2_register *regs = arg; + struct v4l2_dbg_register *regs = arg; unsigned long flags; if (!capable(CAP_SYS_ADMIN)) @@ -286,6 +280,7 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) return -EINVAL; spin_lock_irqsave(&cx18_cards_lock, flags); + regs->size = 4; if (cmd == VIDIOC_DBG_G_REGISTER) regs->val = cx18_read_enc(cx, regs->reg); else @@ -295,31 +290,25 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) } static int cx18_g_register(struct file *file, void *fh, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (v4l2_chip_match_host(®->match)) return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER, - reg); - return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER, - reg); + cx18_call_i2c_clients(cx, VIDIOC_DBG_G_REGISTER, reg); + return 0; } static int cx18_s_register(struct file *file, void *fh, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (v4l2_chip_match_host(®->match)) return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER, - reg); - return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER, - reg); + cx18_call_i2c_clients(cx, VIDIOC_DBG_S_REGISTER, reg); + return 0; } #endif diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 637c4d008846..2d81c4d04340 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -1326,11 +1326,11 @@ static int vidioc_s_frequency(struct file *file, void *priv, #ifdef CONFIG_VIDEO_ADV_DEBUG static int vidioc_g_register(struct file *file, void *fh, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_G_REGISTER, reg); @@ -1339,11 +1339,11 @@ static int vidioc_g_register(struct file *file, void *fh, } static int vidioc_s_register(struct file *file, void *fh, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_S_REGISTER, reg); diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 2ad277189da8..88f2fd32bfe3 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -1120,25 +1120,24 @@ static int cx25840_init(struct v4l2_subdev *sd, u32 val) } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; + reg->size = 1; reg->val = cx25840_read(client, reg->reg & 0x0fff); return 0; } -static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1362,7 +1361,7 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val) return 0; } -static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index b93b7ab99d8c..791e69d804f9 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1447,25 +1447,26 @@ static int vidioc_s_frequency (struct file *file, void *priv, #ifdef CONFIG_VIDEO_ADV_DEBUG static int vidioc_g_register (struct file *file, void *fh, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; /* cx2388x has a 24-bit register space */ - reg->val = cx_read(reg->reg&0xffffff); + reg->val = cx_read(reg->reg & 0xffffff); + reg->size = 4; return 0; } static int vidioc_s_register (struct file *file, void *fh, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; - cx_write(reg->reg&0xffffff, reg->val); + cx_write(reg->reg & 0xffffff, reg->val); return 0; } #endif diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 9cb7c64a88fa..416b691c33c1 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1154,7 +1154,7 @@ static int em28xx_reg_len(int reg) } static int vidioc_g_chip_ident(struct file *file, void *priv, - struct v4l2_chip_ident *chip) + struct v4l2_dbg_chip_ident *chip) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; @@ -1162,20 +1162,20 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, chip->ident = V4L2_IDENT_NONE; chip->revision = 0; - em28xx_i2c_call_clients(dev, VIDIOC_G_CHIP_IDENT, chip); + em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_CHIP_IDENT, chip); return 0; } static int vidioc_g_register(struct file *file, void *priv, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; int ret; - switch (reg->match_type) { + switch (reg->match.type) { case V4L2_CHIP_MATCH_AC97: mutex_lock(&dev->lock); ret = em28xx_read_ac97(dev, reg->reg); @@ -1184,6 +1184,7 @@ static int vidioc_g_register(struct file *file, void *priv, return ret; reg->val = ret; + reg->size = 1; return 0; case V4L2_CHIP_MATCH_I2C_DRIVER: em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_REGISTER, reg); @@ -1192,12 +1193,13 @@ static int vidioc_g_register(struct file *file, void *priv, /* Not supported yet */ return -EINVAL; default: - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; } /* Match host */ - if (em28xx_reg_len(reg->reg) == 1) { + reg->size = em28xx_reg_len(reg->reg); + if (reg->size == 1) { mutex_lock(&dev->lock); ret = em28xx_read_reg(dev, reg->reg); mutex_unlock(&dev->lock); @@ -1207,7 +1209,7 @@ static int vidioc_g_register(struct file *file, void *priv, reg->val = ret; } else { - __le64 val = 0; + __le16 val = 0; mutex_lock(&dev->lock); ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, reg->reg, (char *)&val, 2); @@ -1215,21 +1217,21 @@ static int vidioc_g_register(struct file *file, void *priv, if (ret < 0) return ret; - reg->val = le64_to_cpu(val); + reg->val = le16_to_cpu(val); } return 0; } static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - __le64 buf; + __le16 buf; int rc; - switch (reg->match_type) { + switch (reg->match.type) { case V4L2_CHIP_MATCH_AC97: mutex_lock(&dev->lock); rc = em28xx_write_ac97(dev, reg->reg, reg->val); @@ -1243,12 +1245,12 @@ static int vidioc_s_register(struct file *file, void *priv, /* Not supported yet */ return -EINVAL; default: - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; } /* Match host */ - buf = cpu_to_le64(reg->val); + buf = cpu_to_le16(reg->val); mutex_lock(&dev->lock); rc = em28xx_write_regs(dev, reg->reg, (char *)&buf, diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 08b762951759..e8e5921cdc34 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -902,18 +902,19 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) } if (hw & IVTV_HW_SAA711X) { - struct v4l2_chip_ident v = { V4L2_CHIP_MATCH_I2C_DRIVER, I2C_DRIVERID_SAA711X }; + struct v4l2_dbg_chip_ident v; /* determine the exact saa711x model */ itv->hw_flags &= ~IVTV_HW_SAA711X; + v.match.type = V4L2_CHIP_MATCH_I2C_DRIVER; + strlcpy(v.match.name, "saa7115", sizeof(v.match.name)); ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v); if (v.ident == V4L2_IDENT_SAA7114) { itv->hw_flags |= IVTV_HW_SAA7114; /* VBI is not yet supported by the saa7114 driver. */ itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE); - } - else { + } else { itv->hw_flags |= IVTV_HW_SAA7115; } itv->vbi.raw_decoder_line_size = 1443; diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 1f6ca93b9840..f6b3ef6e691b 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -674,19 +674,19 @@ static int ivtv_s_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_f return ret; } -static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_chip_ident *chip) +static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip) { struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; chip->ident = V4L2_IDENT_NONE; chip->revision = 0; - if (chip->match_type == V4L2_CHIP_MATCH_HOST) { - if (v4l2_chip_match_host(chip->match_type, chip->match_chip)) + if (chip->match.type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(&chip->match)) chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; return 0; } - if (chip->match_type != V4L2_CHIP_MATCH_I2C_DRIVER && - chip->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; /* TODO: is this correct? */ return ivtv_call_all_err(itv, core, g_chip_ident, chip); @@ -695,7 +695,7 @@ static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_chip_ident #ifdef CONFIG_VIDEO_ADV_DEBUG static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) { - struct v4l2_register *regs = arg; + struct v4l2_dbg_register *regs = arg; volatile u8 __iomem *reg_start; if (!capable(CAP_SYS_ADMIN)) @@ -710,6 +710,7 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) else return -EINVAL; + regs->size = 4; if (cmd == VIDIOC_DBG_G_REGISTER) regs->val = readl(regs->reg + reg_start); else @@ -717,11 +718,11 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) return 0; } -static int ivtv_g_register(struct file *file, void *fh, struct v4l2_register *reg) +static int ivtv_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (v4l2_chip_match_host(®->match)) return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg); /* TODO: subdev errors should not be ignored, this should become a subdev helper function. */ @@ -729,11 +730,11 @@ static int ivtv_g_register(struct file *file, void *fh, struct v4l2_register *re return 0; } -static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *reg) +static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (v4l2_chip_match_host(®->match)) return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg); /* TODO: subdev errors should not be ignored, this should become a subdev helper function. */ diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index 07be14a9fe7b..de397ef57b44 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c @@ -80,29 +80,28 @@ static int m52790_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *r } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct m52790_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (reg->reg != 0) return -EINVAL; + reg->size = 1; reg->val = state->input | state->output; return 0; } -static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct m52790_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -115,7 +114,7 @@ static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) } #endif -static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index b8577ade4050..4d7a91852117 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -733,7 +733,7 @@ static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) return 0; } -static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct msp_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 1a1a12453672..c1bf75ef2741 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -343,14 +343,14 @@ static int mt9m001_try_fmt(struct soc_camera_device *icd, } static int mt9m001_get_chip_id(struct soc_camera_device *icd, - struct v4l2_chip_ident *id) + struct v4l2_dbg_chip_ident *id) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match_chip != mt9m001->client->addr) + if (id->match.addr != mt9m001->client->addr) return -ENODEV; id->ident = mt9m001->model; @@ -361,16 +361,17 @@ static int mt9m001_get_chip_id(struct soc_camera_device *icd, #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9m001_get_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match_chip != mt9m001->client->addr) + if (reg->match.addr != mt9m001->client->addr) return -ENODEV; + reg->size = 2; reg->val = reg_read(icd, reg->reg); if (reg->val > 0xffff) @@ -380,14 +381,14 @@ static int mt9m001_get_register(struct soc_camera_device *icd, } static int mt9m001_set_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match_chip != mt9m001->client->addr) + if (reg->match.addr != mt9m001->client->addr) return -ENODEV; if (reg_write(icd, reg->reg, reg->val) < 0) diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index c89ea41fe259..5b8e20979cce 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -514,14 +514,14 @@ static int mt9m111_try_fmt(struct soc_camera_device *icd, } static int mt9m111_get_chip_id(struct soc_camera_device *icd, - struct v4l2_chip_ident *id) + struct v4l2_dbg_chip_ident *id) { struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match_chip != mt9m111->client->addr) + if (id->match.addr != mt9m111->client->addr) return -ENODEV; id->ident = mt9m111->model; @@ -532,18 +532,19 @@ static int mt9m111_get_chip_id(struct soc_camera_device *icd, #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9m111_get_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { int val; struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) return -EINVAL; - if (reg->match_chip != mt9m111->client->addr) + if (reg->match.addr != mt9m111->client->addr) return -ENODEV; val = mt9m111_reg_read(icd, reg->reg); + reg->size = 2; reg->val = (u64)val; if (reg->val > 0xffff) @@ -553,14 +554,14 @@ static int mt9m111_get_register(struct soc_camera_device *icd, } static int mt9m111_set_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) return -EINVAL; - if (reg->match_chip != mt9m111->client->addr) + if (reg->match.addr != mt9m111->client->addr) return -ENODEV; if (mt9m111_reg_write(icd, reg->reg, reg->val) < 0) diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 1a9d53966d06..349d8e365530 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -326,14 +326,14 @@ static int mt9t031_try_fmt(struct soc_camera_device *icd, } static int mt9t031_get_chip_id(struct soc_camera_device *icd, - struct v4l2_chip_ident *id) + struct v4l2_dbg_chip_ident *id) { struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match_chip != mt9t031->client->addr) + if (id->match.addr != mt9t031->client->addr) return -ENODEV; id->ident = mt9t031->model; @@ -344,14 +344,14 @@ static int mt9t031_get_chip_id(struct soc_camera_device *icd, #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9t031_get_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match_chip != mt9t031->client->addr) + if (reg->match.addr != mt9t031->client->addr) return -ENODEV; reg->val = reg_read(icd, reg->reg); @@ -363,14 +363,14 @@ static int mt9t031_get_register(struct soc_camera_device *icd, } static int mt9t031_set_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match_chip != mt9t031->client->addr) + if (reg->match.addr != mt9t031->client->addr) return -ENODEV; if (reg_write(icd, reg->reg, reg->val) < 0) diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 14a5f9c21ffa..b04c8cb1644d 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -422,14 +422,14 @@ static int mt9v022_try_fmt(struct soc_camera_device *icd, } static int mt9v022_get_chip_id(struct soc_camera_device *icd, - struct v4l2_chip_ident *id) + struct v4l2_dbg_chip_ident *id) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match_chip != mt9v022->client->addr) + if (id->match.addr != mt9v022->client->addr) return -ENODEV; id->ident = mt9v022->model; @@ -440,16 +440,17 @@ static int mt9v022_get_chip_id(struct soc_camera_device *icd, #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9v022_get_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match_chip != mt9v022->client->addr) + if (reg->match.addr != mt9v022->client->addr) return -ENODEV; + reg->size = 2; reg->val = reg_read(icd, reg->reg); if (reg->val > 0xffff) @@ -459,14 +460,14 @@ static int mt9v022_get_register(struct soc_camera_device *icd, } static int mt9v022_set_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match_chip != mt9v022->client->addr) + if (reg->match.addr != mt9v022->client->addr) return -ENODEV; if (reg_write(icd, reg->reg, reg->val) < 0) diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index ea032f5f2f41..ca26b0c50cf2 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -1310,7 +1310,7 @@ static int ov7670_command(struct i2c_client *client, unsigned int cmd, void *arg) { switch (cmd) { - case VIDIOC_G_CHIP_IDENT: + case VIDIOC_DBG_G_CHIP_IDENT: return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0); case VIDIOC_INT_RESET: diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 54b736fcc07a..3c9e0ba974e9 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -724,7 +724,7 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) } static int ov772x_get_chip_id(struct soc_camera_device *icd, - struct v4l2_chip_ident *id) + struct v4l2_dbg_chip_ident *id) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); @@ -736,11 +736,12 @@ static int ov772x_get_chip_id(struct soc_camera_device *icd, #ifdef CONFIG_VIDEO_ADV_DEBUG static int ov772x_get_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); int ret; + reg->size = 1; if (reg->reg > 0xff) return -EINVAL; @@ -754,7 +755,7 @@ static int ov772x_get_register(struct soc_camera_device *icd, } static int ov772x_set_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 4358079f1966..8fb92ac78c7b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -4732,26 +4732,25 @@ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) int pvr2_hdw_register_access(struct pvr2_hdw *hdw, - u32 match_type, u32 match_chip, u64 reg_id, - int setFl,u64 *val_ptr) + struct v4l2_dbg_match *match, u64 reg_id, + int setFl, u64 *val_ptr) { #ifdef CONFIG_VIDEO_ADV_DEBUG struct pvr2_i2c_client *cp; - struct v4l2_register req; + struct v4l2_dbg_register req; int stat = 0; int okFl = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - req.match_type = match_type; - req.match_chip = match_chip; + req.match = *match; req.reg = reg_id; if (setFl) req.val = *val_ptr; mutex_lock(&hdw->i2c_list_lock); do { list_for_each_entry(cp, &hdw->i2c_clients, list) { if (!v4l2_chip_match_i2c_client( cp->client, - req.match_type, req.match_chip)) { + &req.match)) { continue; } stat = pvr2_i2c_client_cmd( diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 49482d1f2b28..1b4fec337c6b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -242,8 +242,8 @@ void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *, setFl - true to set the register, false to read it val_ptr - storage location for source / result. */ int pvr2_hdw_register_access(struct pvr2_hdw *, - u32 match_type, u32 match_chip,u64 reg_id, - int setFl,u64 *val_ptr); + struct v4l2_dbg_match *match, u64 reg_id, + int setFl, u64 *val_ptr); /* The following entry points are all lower level things you normally don't want to worry about. */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index b9aedceb2c44..878fd52a73b3 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -851,11 +851,11 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_DBG_G_REGISTER: { u64 val; - struct v4l2_register *req = (struct v4l2_register *)arg; + struct v4l2_dbg_register *req = (struct v4l2_dbg_register *)arg; if (cmd == VIDIOC_DBG_S_REGISTER) val = req->val; ret = pvr2_hdw_register_access( - hdw,req->match_type,req->match_chip,req->reg, - cmd == VIDIOC_DBG_S_REGISTER,&val); + hdw, &req->match, req->reg, + cmd == VIDIOC_DBG_S_REGISTER, &val); if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val; break; } diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 22708ecdf1bb..46c796c3fec8 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -1371,25 +1371,24 @@ static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_dat } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; reg->val = saa711x_read(sd, reg->reg & 0xff); + reg->size = 1; return 0; } -static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1398,7 +1397,7 @@ static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) } #endif -static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct saa711x_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index bfc85654795e..d6848f7a503b 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -623,25 +623,24 @@ static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_v } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; reg->val = saa7127_read(sd, reg->reg & 0xff); + reg->size = 1; return 0; } -static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -650,7 +649,7 @@ static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) } #endif -static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct saa7127_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c index 1fb6eccdade3..1fee6e84a512 100644 --- a/drivers/media/video/saa7134/saa6752hs.c +++ b/drivers/media/video/saa7134/saa6752hs.c @@ -838,7 +838,7 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) h->standard = *((v4l2_std_id *) arg); break; - case VIDIOC_G_CHIP_IDENT: + case VIDIOC_DBG_G_CHIP_IDENT: return v4l2_chip_ident_i2c_client(client, arg, h->chip, h->revision); diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 3beba480137f..c9d8beb87a60 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -405,7 +405,7 @@ static int empress_querymenu(struct file *file, void *priv, } static int empress_g_chip_ident(struct file *file, void *fh, - struct v4l2_chip_ident *chip) + struct v4l2_dbg_chip_ident *chip) { struct saa7134_dev *dev = file->private_data; @@ -413,12 +413,12 @@ static int empress_g_chip_ident(struct file *file, void *fh, chip->revision = 0; if (dev->mpeg_i2c_client == NULL) return -EINVAL; - if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER && - chip->match_chip == I2C_DRIVERID_SAA6752HS) - return saa7134_i2c_call_saa6752(dev, VIDIOC_G_CHIP_IDENT, chip); - if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR && - chip->match_chip == dev->mpeg_i2c_client->addr) - return saa7134_i2c_call_saa6752(dev, VIDIOC_G_CHIP_IDENT, chip); + if (chip->match.type == V4L2_CHIP_MATCH_I2C_DRIVER && + !strcmp(chip->match.name, "saa6752hs")) + return saa7134_i2c_call_saa6752(dev, VIDIOC_DBG_G_CHIP_IDENT, chip); + if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR && + chip->match.addr == dev->mpeg_i2c_client->addr) + return saa7134_i2c_call_saa6752(dev, VIDIOC_DBG_G_CHIP_IDENT, chip); return -EINVAL; } diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 6b2ab57538ee..a1f7e351f572 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -2247,24 +2247,25 @@ static int saa7134_g_parm(struct file *file, void *fh, #ifdef CONFIG_VIDEO_ADV_DEBUG static int vidioc_g_register (struct file *file, void *priv, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; reg->val = saa_readb(reg->reg); + reg->size = 1; return 0; } static int vidioc_s_register (struct file *file, void *priv, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; saa_writeb(reg->reg&0xffffff, reg->val); return 0; diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index 9befca65905e..454ad1dd7507 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -1171,25 +1171,26 @@ static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; reg->val = saa717x_read(sd, reg->reg); + reg->size = 1; return 0; } -static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); u16 addr = reg->reg & 0xffff; u8 val = reg->val & 0xff; - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 9986e02bcf1a..fcb05f06de8f 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -699,7 +699,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, } static int soc_camera_g_chip_ident(struct file *file, void *fh, - struct v4l2_chip_ident *id) + struct v4l2_dbg_chip_ident *id) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; @@ -712,7 +712,7 @@ static int soc_camera_g_chip_ident(struct file *file, void *fh, #ifdef CONFIG_VIDEO_ADV_DEBUG static int soc_camera_g_register(struct file *file, void *fh, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; @@ -724,7 +724,7 @@ static int soc_camera_g_register(struct file *file, void *fh, } static int soc_camera_s_register(struct file *file, void *fh, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index d0c794da735b..5aeccb301cea 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1762,7 +1762,7 @@ static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr return 0; } -static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index a388a9f0cb18..2cd64ef27b95 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -963,7 +963,7 @@ static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_chip_ident *chip) + struct v4l2_dbg_chip_ident *chip) { int rev; struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -977,25 +977,24 @@ static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, #ifdef CONFIG_VIDEO_ADV_DEBUG -static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; reg->val = tvp5150_read(sd, reg->reg & 0xff); + reg->size = 1; return 0; } -static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index d5cdc4be1a35..52c0357faa5d 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -575,7 +575,7 @@ static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd) } static int tw9910_get_chip_id(struct soc_camera_device *icd, - struct v4l2_chip_ident *id) + struct v4l2_dbg_chip_ident *id) { id->ident = V4L2_IDENT_TW9910; id->revision = 0; @@ -606,7 +606,7 @@ static int tw9910_enum_input(struct soc_camera_device *icd, #ifdef CONFIG_VIDEO_ADV_DEBUG static int tw9910_get_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); int ret; @@ -627,7 +627,7 @@ static int tw9910_get_register(struct soc_camera_device *icd, } static int tw9910_set_register(struct soc_camera_device *icd, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index 7a609a3a6dbe..4f16effb530f 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -147,7 +147,7 @@ static int upd64031a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing return upd64031a_s_frequency(sd, NULL); } -static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -162,25 +162,24 @@ static int upd64031a_log_status(struct v4l2_subdev *sd) } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; reg->val = upd64031a_read(sd, reg->reg & 0xff); + reg->size = 1; return 0; } -static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index 58412cb9c01a..4b712f69d1b7 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -120,25 +120,24 @@ static int upd64083_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; reg->val = upd64083_read(sd, reg->reg & 0xff); + reg->size = 1; return 0; } -static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -147,7 +146,7 @@ static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg } #endif -static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 7c61c6d5cede..2be5e47ed081 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -477,12 +477,12 @@ static int usbvision_v4l2_close(struct file *file) */ #ifdef CONFIG_VIDEO_ADV_DEBUG static int vidioc_g_register (struct file *file, void *priv, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct usb_usbvision *usbvision = video_drvdata(file); int errCode; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; /* NT100x has a 8-bit register space */ errCode = usbvision_read_reg(usbvision, reg->reg&0xff); @@ -492,16 +492,17 @@ static int vidioc_g_register (struct file *file, void *priv, return errCode; } reg->val = errCode; + reg->size = 1; return 0; } static int vidioc_s_register (struct file *file, void *priv, - struct v4l2_register *reg) + struct v4l2_dbg_register *reg) { struct usb_usbvision *usbvision = video_drvdata(file); int errCode; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + if (!v4l2_chip_match_host(®->match)) return -EINVAL; /* NT100x has a 8-bit register space */ errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index c676b0b0f708..b8f2be8d5c0e 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -797,11 +797,11 @@ u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id) } EXPORT_SYMBOL(v4l2_ctrl_next); -int v4l2_chip_match_host(u32 match_type, u32 match_chip) +int v4l2_chip_match_host(const struct v4l2_dbg_match *match) { - switch (match_type) { + switch (match->type) { case V4L2_CHIP_MATCH_HOST: - return match_chip == 0; + return match->addr == 0; default: return 0; } @@ -809,23 +809,34 @@ int v4l2_chip_match_host(u32 match_type, u32 match_chip) EXPORT_SYMBOL(v4l2_chip_match_host); #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -int v4l2_chip_match_i2c_client(struct i2c_client *c, u32 match_type, u32 match_chip) +int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match) { - switch (match_type) { + int len; + + if (c == NULL || match == NULL) + return 0; + + switch (match->type) { case V4L2_CHIP_MATCH_I2C_DRIVER: - return (c != NULL && c->driver != NULL && c->driver->id == match_chip); + if (c->driver == NULL || c->driver->driver.name == NULL) + return 0; + len = strlen(c->driver->driver.name); + /* legacy drivers have a ' suffix, don't try to match that */ + if (len && c->driver->driver.name[len - 1] == '\'') + len--; + return len && !strncmp(c->driver->driver.name, match->name, len); case V4L2_CHIP_MATCH_I2C_ADDR: - return (c != NULL && c->addr == match_chip); + return c->addr == match->addr; default: return 0; } } EXPORT_SYMBOL(v4l2_chip_match_i2c_client); -int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_chip_ident *chip, +int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_dbg_chip_ident *chip, u32 ident, u32 revision) { - if (!v4l2_chip_match_i2c_client(c, chip->match_type, chip->match_chip)) + if (!v4l2_chip_match_i2c_client(c, &chip->match)) return 0; if (chip->ident == V4L2_IDENT_NONE) { chip->ident = ident; diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index ec81b9737bd7..110376be5d2b 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -1046,7 +1046,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_TRY_ENCODER_CMD: case VIDIOC_DBG_S_REGISTER: case VIDIOC_DBG_G_REGISTER: - case VIDIOC_G_CHIP_IDENT: + case VIDIOC_DBG_G_CHIP_IDENT: + case VIDIOC_G_CHIP_IDENT_OLD: case VIDIOC_S_HW_FREQ_SEEK: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 8f629ef5b9ec..52d687b165e0 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -266,7 +266,7 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", - [_IOC_NR(VIDIOC_G_CHIP_IDENT)] = "VIDIOC_G_CHIP_IDENT", + [_IOC_NR(VIDIOC_DBG_G_CHIP_IDENT)] = "VIDIOC_DBG_G_CHIP_IDENT", [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", #endif }; @@ -1720,7 +1720,7 @@ static long __video_do_ioctl(struct file *file, #ifdef CONFIG_VIDEO_ADV_DEBUG case VIDIOC_DBG_G_REGISTER: { - struct v4l2_register *p = arg; + struct v4l2_dbg_register *p = arg; if (!capable(CAP_SYS_ADMIN)) ret = -EPERM; @@ -1730,7 +1730,7 @@ static long __video_do_ioctl(struct file *file, } case VIDIOC_DBG_S_REGISTER: { - struct v4l2_register *p = arg; + struct v4l2_dbg_register *p = arg; if (!capable(CAP_SYS_ADMIN)) ret = -EPERM; @@ -1739,9 +1739,9 @@ static long __video_do_ioctl(struct file *file, break; } #endif - case VIDIOC_G_CHIP_IDENT: + case VIDIOC_DBG_G_CHIP_IDENT: { - struct v4l2_chip_ident *p = arg; + struct v4l2_dbg_chip_ident *p = arg; if (!ops->vidioc_g_chip_ident) break; @@ -1750,6 +1750,11 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision); break; } + case VIDIOC_G_CHIP_IDENT_OLD: + printk(KERN_ERR "VIDIOC_G_CHIP_IDENT has been deprecated and will disappear in 2.6.30.\n"); + printk(KERN_ERR "It is a debugging ioctl and must not be used in applications!\n"); + return -EINVAL; + case VIDIOC_S_HW_FREQ_SEEK: { struct v4l2_hw_freq_seek *p = arg; diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index e3612f29d0df..fbe9cc0d433a 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -37,7 +37,7 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) return v4l2_subdev_call(sd, core, queryctrl, arg); case VIDIOC_LOG_STATUS: return v4l2_subdev_call(sd, core, log_status); - case VIDIOC_G_CHIP_IDENT: + case VIDIOC_DBG_G_CHIP_IDENT: return v4l2_subdev_call(sd, core, g_chip_ident, arg); case VIDIOC_INT_S_STANDBY: return v4l2_subdev_call(sd, core, s_standby, arg ? (*(u32 *)arg) : 0); diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c index f72b859486ad..5d73f66d9f55 100644 --- a/drivers/media/video/vp27smpx.c +++ b/drivers/media/video/vp27smpx.c @@ -113,7 +113,7 @@ static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index 12a31e7a5f6d..f2864d5cd180 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -233,7 +233,7 @@ static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) return -EINVAL; } -static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index d0220b0ec0bc..53fcd42843e0 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -130,7 +130,7 @@ static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return 0; } -static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 1f126e30766c..5571dbe1c0ad 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1370,25 +1370,41 @@ struct v4l2_streamparm { /* * A D V A N C E D D E B U G G I N G * - * NOTE: EXPERIMENTAL API + * NOTE: EXPERIMENTAL API, NEVER RELY ON THIS IN APPLICATIONS! + * FOR DEBUGGING, TESTING AND INTERNAL USE ONLY! */ /* VIDIOC_DBG_G_REGISTER and VIDIOC_DBG_S_REGISTER */ #define V4L2_CHIP_MATCH_HOST 0 /* Match against chip ID on host (0 for the host) */ -#define V4L2_CHIP_MATCH_I2C_DRIVER 1 /* Match against I2C driver ID */ +#define V4L2_CHIP_MATCH_I2C_DRIVER 1 /* Match against I2C driver name */ #define V4L2_CHIP_MATCH_I2C_ADDR 2 /* Match against I2C 7-bit address */ #define V4L2_CHIP_MATCH_AC97 3 /* Match against anciliary AC97 chip */ -struct v4l2_register { - __u32 match_type; /* Match type */ - __u32 match_chip; /* Match this chip, meaning determined by match_type */ +struct v4l2_dbg_match { + __u32 type; /* Match type */ + union { /* Match this chip, meaning determined by type */ + __u32 addr; + char name[32]; + }; +} __attribute__ ((packed)); + +struct v4l2_dbg_register { + struct v4l2_dbg_match match; + __u32 size; /* register size in bytes */ __u64 reg; __u64 val; -}; +} __attribute__ ((packed)); + +/* VIDIOC_DBG_G_CHIP_IDENT */ +struct v4l2_dbg_chip_ident { + struct v4l2_dbg_match match; + __u32 ident; /* chip identifier as specified in */ + __u32 revision; /* chip revision, chip specific */ +} __attribute__ ((packed)); -/* VIDIOC_G_CHIP_IDENT */ -struct v4l2_chip_ident { +/* VIDIOC_G_CHIP_IDENT_OLD: Deprecated, do not use */ +struct v4l2_chip_ident_old { __u32 match_type; /* Match type */ __u32 match_chip; /* Match this chip, meaning determined by match_type */ __u32 ident; /* chip identifier as specified in */ @@ -1460,13 +1476,22 @@ struct v4l2_chip_ident { #define VIDIOC_G_ENC_INDEX _IOR('V', 76, struct v4l2_enc_idx) #define VIDIOC_ENCODER_CMD _IOWR('V', 77, struct v4l2_encoder_cmd) #define VIDIOC_TRY_ENCODER_CMD _IOWR('V', 78, struct v4l2_encoder_cmd) +#endif -/* Experimental, only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */ -#define VIDIOC_DBG_S_REGISTER _IOW('V', 79, struct v4l2_register) -#define VIDIOC_DBG_G_REGISTER _IOWR('V', 80, struct v4l2_register) - -#define VIDIOC_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_chip_ident) +#if 1 +/* Experimental, meant for debugging, testing and internal use. + Only implemented if CONFIG_VIDEO_ADV_DEBUG is defined. + You must be root to use these ioctls. Never use these in applications! */ +#define VIDIOC_DBG_S_REGISTER _IOW('V', 79, struct v4l2_dbg_register) +#define VIDIOC_DBG_G_REGISTER _IOWR('V', 80, struct v4l2_dbg_register) + +/* Experimental, meant for debugging, testing and internal use. + Never use this ioctl in applications! */ +#define VIDIOC_DBG_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_dbg_chip_ident) +/* This is deprecated and will go away in 2.6.30 */ +#define VIDIOC_G_CHIP_IDENT_OLD _IOWR('V', 81, struct v4l2_chip_ident_old) #endif + #define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek) /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 425b6a98c95c..7440d9250665 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -164,12 +164,12 @@ struct soc_camera_ops { unsigned long (*query_bus_param)(struct soc_camera_device *); int (*set_bus_param)(struct soc_camera_device *, unsigned long); int (*get_chip_id)(struct soc_camera_device *, - struct v4l2_chip_ident *); + struct v4l2_dbg_chip_ident *); int (*set_std)(struct soc_camera_device *, v4l2_std_id *); int (*enum_input)(struct soc_camera_device *, struct v4l2_input *); #ifdef CONFIG_VIDEO_ADV_DEBUG - int (*get_register)(struct soc_camera_device *, struct v4l2_register *); - int (*set_register)(struct soc_camera_device *, struct v4l2_register *); + int (*get_register)(struct soc_camera_device *, struct v4l2_dbg_register *); + int (*set_register)(struct soc_camera_device *, struct v4l2_dbg_register *); #endif int (*get_control)(struct soc_camera_device *, struct v4l2_control *); int (*set_control)(struct soc_camera_device *, struct v4l2_control *); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 43dbb659f1f5..9aaf652b20ef 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -2,7 +2,7 @@ v4l2 chip identifiers header This header provides a list of chip identifiers that can be returned - through the VIDIOC_G_CHIP_IDENT ioctl. + through the VIDIOC_DBG_G_CHIP_IDENT ioctl. Copyright (C) 2007 Hans Verkuil @@ -24,7 +24,7 @@ #ifndef V4L2_CHIP_IDENT_H_ #define V4L2_CHIP_IDENT_H_ -/* VIDIOC_G_CHIP_IDENT: identifies the actual chip installed on the board */ +/* VIDIOC_DBG_G_CHIP_IDENT: identifies the actual chip installed on the board */ enum { /* general idents: reserved range 0-49 */ V4L2_IDENT_NONE = 0, /* No chip matched */ diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index f99c866d8c37..95e74f1874e1 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -114,10 +114,10 @@ u32 v4l2_ctrl_next(const u32 * const *ctrl_classes, u32 id); /* Register/chip ident helper function */ struct i2c_client; /* forward reference */ -int v4l2_chip_match_i2c_client(struct i2c_client *c, u32 id_type, u32 chip_id); -int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_chip_ident *chip, +int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match); +int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_dbg_chip_ident *chip, u32 ident, u32 revision); -int v4l2_chip_match_host(u32 id_type, u32 chip_id); +int v4l2_chip_match_host(const struct v4l2_dbg_match *match); /* ------------------------------------------------------------------------- */ diff --git a/include/media/v4l2-int-device.h b/include/media/v4l2-int-device.h index ecda3c725837..fbf585561570 100644 --- a/include/media/v4l2-int-device.h +++ b/include/media/v4l2-int-device.h @@ -219,7 +219,7 @@ enum v4l2_int_ioctl_num { vidioc_int_reset_num, /* VIDIOC_INT_INIT */ vidioc_int_init_num, - /* VIDIOC_INT_G_CHIP_IDENT */ + /* VIDIOC_DBG_G_CHIP_IDENT */ vidioc_int_g_chip_ident_num, /* diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index bf0e723a99c1..b01c044868d0 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -225,12 +225,12 @@ struct v4l2_ioctl_ops { /* Debugging ioctls */ #ifdef CONFIG_VIDEO_ADV_DEBUG int (*vidioc_g_register) (struct file *file, void *fh, - struct v4l2_register *reg); + struct v4l2_dbg_register *reg); int (*vidioc_s_register) (struct file *file, void *fh, - struct v4l2_register *reg); + struct v4l2_dbg_register *reg); #endif int (*vidioc_g_chip_ident) (struct file *file, void *fh, - struct v4l2_chip_ident *chip); + struct v4l2_dbg_chip_ident *chip); int (*vidioc_enum_framesizes) (struct file *file, void *fh, struct v4l2_frmsizeenum *fsize); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 2517344313b6..37b09e56e943 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -69,7 +69,7 @@ struct tuner_setup; not yet implemented) since ops provide proper type-checking. */ struct v4l2_subdev_core_ops { - int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip); + int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip); int (*log_status)(struct v4l2_subdev *sd); int (*init)(struct v4l2_subdev *sd, u32 val); int (*s_standby)(struct v4l2_subdev *sd, u32 standby); @@ -81,8 +81,8 @@ struct v4l2_subdev_core_ops { int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm); long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg); #ifdef CONFIG_VIDEO_ADV_DEBUG - int (*g_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); - int (*s_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); + int (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg); + int (*s_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg); #endif }; -- cgit v1.2.3 From cb889a2f3515b140bef193cf6ffcdb099349b8aa Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Wed, 31 Dec 2008 14:11:23 -0300 Subject: V4L/DVB (10164): Add missing S2 caps flag to S2API The attached patch adds a capability flag that allows an application to determine whether a particular device can handle "second generation modulation" transponders. This is necessary in order for applications to be able to decide which device to use for a given channel in a multi device environment, where DVB-S and DVB-S2 devices are mixed. It is assumed that a device capable of handling "second generation modulation" can implicitly handle "first generation modulation". The flag is not named anything with DVBS2 in order to allow its use with future DVBT2 devices as well (should they ever come). Signed-off by: Klaus Schmidinger Acked-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- include/linux/dvb/frontend.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 79a8ed8e6a7d..926d28d526e7 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -63,6 +63,7 @@ typedef enum fe_caps { FE_CAN_8VSB = 0x200000, FE_CAN_16VSB = 0x400000, FE_HAS_EXTENDED_CAPS = 0x800000, // We need more bitspace for newer APIs, indicate this. + FE_CAN_2G_MODULATION = 0x10000000, // frontend supports "2nd generation modulation" (DVB-S2) FE_NEEDS_BENDING = 0x20000000, // not supported anymore, don't use (frontend requires frequency bending) FE_CAN_RECOVER = 0x40000000, // frontend can recover from a cable unplug automatically FE_CAN_MUTE_TS = 0x80000000 // frontend can stop spurious TS data output -- cgit v1.2.3 From e4cda3e0728156c6be1d03e72ef20ea811da4ad5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 31 Dec 2008 14:26:57 -0300 Subject: V4L/DVB (10166): dvb frontend: stop using non-C99 compliant comments Signed-off-by: Mauro Carvalho Chehab --- include/linux/dvb/frontend.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 926d28d526e7..55026b1a40bd 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -62,11 +62,11 @@ typedef enum fe_caps { FE_CAN_HIERARCHY_AUTO = 0x100000, FE_CAN_8VSB = 0x200000, FE_CAN_16VSB = 0x400000, - FE_HAS_EXTENDED_CAPS = 0x800000, // We need more bitspace for newer APIs, indicate this. - FE_CAN_2G_MODULATION = 0x10000000, // frontend supports "2nd generation modulation" (DVB-S2) - FE_NEEDS_BENDING = 0x20000000, // not supported anymore, don't use (frontend requires frequency bending) - FE_CAN_RECOVER = 0x40000000, // frontend can recover from a cable unplug automatically - FE_CAN_MUTE_TS = 0x80000000 // frontend can stop spurious TS data output + FE_HAS_EXTENDED_CAPS = 0x800000, /* We need more bitspace for newer APIs, indicate this. */ + FE_CAN_2G_MODULATION = 0x10000000, /* frontend supports "2nd generation modulation" (DVB-S2) */ + FE_NEEDS_BENDING = 0x20000000, /* not supported anymore, don't use (frontend requires frequency bending) */ + FE_CAN_RECOVER = 0x40000000, /* frontend can recover from a cable unplug automatically */ + FE_CAN_MUTE_TS = 0x80000000 /* frontend can stop spurious TS data output */ } fe_caps_t; @@ -122,15 +122,15 @@ typedef enum fe_sec_mini_cmd { typedef enum fe_status { - FE_HAS_SIGNAL = 0x01, /* found something above the noise level */ - FE_HAS_CARRIER = 0x02, /* found a DVB signal */ - FE_HAS_VITERBI = 0x04, /* FEC is stable */ - FE_HAS_SYNC = 0x08, /* found sync bytes */ - FE_HAS_LOCK = 0x10, /* everything's working... */ - FE_TIMEDOUT = 0x20, /* no lock within the last ~2 seconds */ - FE_REINIT = 0x40 /* frontend was reinitialized, */ -} fe_status_t; /* application is recommended to reset */ - /* DiSEqC, tone and parameters */ + FE_HAS_SIGNAL = 0x01, /* found something above the noise level */ + FE_HAS_CARRIER = 0x02, /* found a DVB signal */ + FE_HAS_VITERBI = 0x04, /* FEC is stable */ + FE_HAS_SYNC = 0x08, /* found sync bytes */ + FE_HAS_LOCK = 0x10, /* everything's working... */ + FE_TIMEDOUT = 0x20, /* no lock within the last ~2 seconds */ + FE_REINIT = 0x40 /* frontend was reinitialized, */ +} fe_status_t; /* application is recommended to reset */ + /* DiSEqC, tone and parameters */ typedef enum fe_spectral_inversion { INVERSION_OFF, -- cgit v1.2.3 From 6680598b44ed3c0052d155522eb21fc5a00de5f3 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 2 Jan 2009 18:53:14 +0100 Subject: Disallow gcc versions 3.{0,1} GCC 3.0 and 3.1 are too old to build a working kernel. Signed-off-by: Ingo Molnar [ This check got dropped as obsolete when I simplified the gcc header inclusion mess in f153b82121b0366fe0e5f9553545cce237335175, but Willy Tarreau reports actually having those old versions still.. -Linus ] Signed-off-by: Linus Torvalds --- include/linux/compiler-gcc3.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/compiler-gcc3.h b/include/linux/compiler-gcc3.h index 2befe6513ce4..8005effc04f1 100644 --- a/include/linux/compiler-gcc3.h +++ b/include/linux/compiler-gcc3.h @@ -2,6 +2,10 @@ #error "Please don't include directly, include instead." #endif +#if __GNUC_MINOR__ < 2 +# error Sorry, your compiler is too old - please upgrade it. +#endif + #if __GNUC_MINOR__ >= 3 # define __used __attribute__((__used__)) #else -- cgit v1.2.3 From 015ab17dc2e9de805c26e74f498b12ee5e8de07e Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 14:04:20 +0000 Subject: intel-iommu: remove some unused struct intel_iommu fields The seg, saved_msg and sysdev fields appear to be unused since before the code was first merged. linux/msi.h is not needed in linux/intel-iommu.h anymore since there is no longer a reference to struct msi_msg. The MSI code in drivers/pci/intel-iommu.c still has linux/msi.h included via linux/dmar.h. linux/sysdev.h isn't needed because there is no reference to struct sys_device. Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 1 - include/linux/intel-iommu.h | 5 ----- 2 files changed, 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 5c8baa43ac9c..8e5a445ba93a 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 3d017cfd245b..1bff7bf1bc2c 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -23,8 +23,6 @@ #define _INTEL_IOMMU_H_ #include -#include -#include #include #include #include @@ -289,7 +287,6 @@ struct intel_iommu { void __iomem *reg; /* Pointer to hardware regs, virtual addr */ u64 cap; u64 ecap; - int seg; u32 gcmd; /* Holds TE, EAFL. Don't need SRTP, SFL, WBF */ spinlock_t register_lock; /* protect register handling */ int seq_id; /* sequence id of the iommu */ @@ -302,8 +299,6 @@ struct intel_iommu { unsigned int irq; unsigned char name[7]; /* Device Name */ - struct msi_msg saved_msg; - struct sys_device sysdev; struct iommu_flush flush; #endif struct q_inval *qi; /* Queued invalidation info */ -- cgit v1.2.3 From 519a05491586dad04e687660e54c57882315b22b Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 14:21:13 +0000 Subject: intel-iommu: make init_dmars() static init_dmars() is not used outside of drivers/pci/intel-iommu.c Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 2 +- include/linux/dma_remapping.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 8e5a445ba93a..95ae3a9aea8a 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1589,7 +1589,7 @@ static inline void iommu_prepare_isa(void) } #endif /* !CONFIG_DMAR_FLPY_WA */ -int __init init_dmars(void) +static int __init init_dmars(void) { struct dmar_drhd_unit *drhd; struct dmar_rmrr_unit *rmrr; diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index 952df39c989d..cf92c4924b8c 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -141,7 +141,6 @@ struct device_domain_info { struct dmar_domain *domain; /* pointer to domain */ }; -extern int init_dmars(void); extern void free_dmar_iommu(struct intel_iommu *iommu); extern int dmar_disabled; -- cgit v1.2.3 From f27be03b271851fd54529f292c0f25b4c1f1a553 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:43 +0000 Subject: intel-iommu: move DMA_32/64BIT_PFN into intel-iommu.c Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 3 +++ include/linux/dma_remapping.h | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 95ae3a9aea8a..6fadbb9bc180 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -53,6 +53,9 @@ #define DOMAIN_MAX_ADDR(gaw) ((((u64)1) << gaw) - 1) +#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) +#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK) +#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK) static void flush_unmaps_timeout(unsigned long data); diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index cf92c4924b8c..2e5a5c0b6acd 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -9,11 +9,6 @@ #define VTD_PAGE_MASK (((u64)-1) << VTD_PAGE_SHIFT) #define VTD_PAGE_ALIGN(addr) (((addr) + VTD_PAGE_SIZE - 1) & VTD_PAGE_MASK) -#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) -#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK) -#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK) - - /* * 0: Present * 1-11: Reserved -- cgit v1.2.3 From 46b08e1a76b758193b0e7b889c6486a16eb1e9e2 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:44 +0000 Subject: intel-iommu: move root entry defs from dma_remapping.h We keep the struct root_entry forward declaration for the pointer in struct intel_iommu. Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 33 +++++++++++++++++++++++++++++++++ include/linux/dma_remapping.h | 34 +--------------------------------- 2 files changed, 34 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 6fadbb9bc180..29bf2d8176e2 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -57,6 +57,39 @@ #define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK) #define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK) +/* + * 0: Present + * 1-11: Reserved + * 12-63: Context Ptr (12 - (haw-1)) + * 64-127: Reserved + */ +struct root_entry { + u64 val; + u64 rsvd1; +}; +#define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry)) +static inline bool root_present(struct root_entry *root) +{ + return (root->val & 1); +} +static inline void set_root_present(struct root_entry *root) +{ + root->val |= 1; +} +static inline void set_root_value(struct root_entry *root, unsigned long value) +{ + root->val |= value & VTD_PAGE_MASK; +} + +static inline struct context_entry * +get_context_addr_from_root(struct root_entry *root) +{ + return (struct context_entry *) + (root_present(root)?phys_to_virt( + root->val & VTD_PAGE_MASK) : + NULL); +} + static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index 2e5a5c0b6acd..d8521662a495 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -9,39 +9,7 @@ #define VTD_PAGE_MASK (((u64)-1) << VTD_PAGE_SHIFT) #define VTD_PAGE_ALIGN(addr) (((addr) + VTD_PAGE_SIZE - 1) & VTD_PAGE_MASK) -/* - * 0: Present - * 1-11: Reserved - * 12-63: Context Ptr (12 - (haw-1)) - * 64-127: Reserved - */ -struct root_entry { - u64 val; - u64 rsvd1; -}; -#define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry)) -static inline bool root_present(struct root_entry *root) -{ - return (root->val & 1); -} -static inline void set_root_present(struct root_entry *root) -{ - root->val |= 1; -} -static inline void set_root_value(struct root_entry *root, unsigned long value) -{ - root->val |= value & VTD_PAGE_MASK; -} - -struct context_entry; -static inline struct context_entry * -get_context_addr_from_root(struct root_entry *root) -{ - return (struct context_entry *) - (root_present(root)?phys_to_virt( - root->val & VTD_PAGE_MASK) : - NULL); -} +struct root_entry; /* * low 64 bits: -- cgit v1.2.3 From 7a8fc25e0cc6e75fa6fdb0a856490e324218550b Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:45 +0000 Subject: intel-iommu: move context entry defs out from dma_remapping.h Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/dma_remapping.h | 38 -------------------------------------- 2 files changed, 38 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 29bf2d8176e2..9d06f4bb6b5e 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -90,6 +90,44 @@ get_context_addr_from_root(struct root_entry *root) NULL); } +/* + * low 64 bits: + * 0: present + * 1: fault processing disable + * 2-3: translation type + * 12-63: address space root + * high 64 bits: + * 0-2: address width + * 3-6: aval + * 8-23: domain id + */ +struct context_entry { + u64 lo; + u64 hi; +}; +#define context_present(c) ((c).lo & 1) +#define context_fault_disable(c) (((c).lo >> 1) & 1) +#define context_translation_type(c) (((c).lo >> 2) & 3) +#define context_address_root(c) ((c).lo & VTD_PAGE_MASK) +#define context_address_width(c) ((c).hi & 7) +#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1)) + +#define context_set_present(c) do {(c).lo |= 1;} while (0) +#define context_set_fault_enable(c) \ + do {(c).lo &= (((u64)-1) << 2) | 1;} while (0) +#define context_set_translation_type(c, val) \ + do { \ + (c).lo &= (((u64)-1) << 4) | 3; \ + (c).lo |= ((val) & 3) << 2; \ + } while (0) +#define CONTEXT_TT_MULTI_LEVEL 0 +#define context_set_address_root(c, val) \ + do {(c).lo |= (val) & VTD_PAGE_MASK; } while (0) +#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0) +#define context_set_domain_id(c, val) \ + do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0) +#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0) + static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index d8521662a495..9a88f7d0262f 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -11,44 +11,6 @@ struct root_entry; -/* - * low 64 bits: - * 0: present - * 1: fault processing disable - * 2-3: translation type - * 12-63: address space root - * high 64 bits: - * 0-2: address width - * 3-6: aval - * 8-23: domain id - */ -struct context_entry { - u64 lo; - u64 hi; -}; -#define context_present(c) ((c).lo & 1) -#define context_fault_disable(c) (((c).lo >> 1) & 1) -#define context_translation_type(c) (((c).lo >> 2) & 3) -#define context_address_root(c) ((c).lo & VTD_PAGE_MASK) -#define context_address_width(c) ((c).hi & 7) -#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1)) - -#define context_set_present(c) do {(c).lo |= 1;} while (0) -#define context_set_fault_enable(c) \ - do {(c).lo &= (((u64)-1) << 2) | 1;} while (0) -#define context_set_translation_type(c, val) \ - do { \ - (c).lo &= (((u64)-1) << 4) | 3; \ - (c).lo |= ((val) & 3) << 2; \ - } while (0) -#define CONTEXT_TT_MULTI_LEVEL 0 -#define context_set_address_root(c, val) \ - do {(c).lo |= (val) & VTD_PAGE_MASK; } while (0) -#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0) -#define context_set_domain_id(c, val) \ - do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0) -#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0) - /* * 0: readable * 1: writable -- cgit v1.2.3 From 622ba12a4c2148999bda9b891bfd0c6ddcb6c57e Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:46 +0000 Subject: intel-iommu: move DMA PTE defs out of dma_remapping.h DMA_PTE_READ/WRITE are needed by kvm. Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 22 ++++++++++++++++++++++ include/linux/dma_remapping.h | 22 ---------------------- 2 files changed, 22 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 9d06f4bb6b5e..26c5402b6f7c 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -128,6 +128,28 @@ struct context_entry { do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0) #define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0) +/* + * 0: readable + * 1: writable + * 2-6: reserved + * 7: super page + * 8-11: available + * 12-63: Host physcial address + */ +struct dma_pte { + u64 val; +}; +#define dma_clear_pte(p) do {(p).val = 0;} while (0) + +#define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while (0) +#define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while (0) +#define dma_set_pte_prot(p, prot) \ + do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0) +#define dma_pte_addr(p) ((p).val & VTD_PAGE_MASK) +#define dma_set_pte_addr(p, addr) do {\ + (p).val |= ((addr) & VTD_PAGE_MASK); } while (0) +#define dma_pte_present(p) (((p).val & 3) != 0) + static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index 9a88f7d0262f..9d5874e3bec9 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -11,31 +11,9 @@ struct root_entry; -/* - * 0: readable - * 1: writable - * 2-6: reserved - * 7: super page - * 8-11: available - * 12-63: Host physcial address - */ -struct dma_pte { - u64 val; -}; -#define dma_clear_pte(p) do {(p).val = 0;} while (0) - #define DMA_PTE_READ (1) #define DMA_PTE_WRITE (2) -#define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while (0) -#define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while (0) -#define dma_set_pte_prot(p, prot) \ - do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0) -#define dma_pte_addr(p) ((p).val & VTD_PAGE_MASK) -#define dma_set_pte_addr(p, addr) do {\ - (p).val |= ((addr) & VTD_PAGE_MASK); } while (0) -#define dma_pte_present(p) (((p).val & 3) != 0) - struct intel_iommu; struct dmar_domain { -- cgit v1.2.3 From 99126f7ce14aff5f9371b2fa81fddb82be815794 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:47 +0000 Subject: intel-iommu: move struct dmar_domain def out dma_remapping.h Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 18 ++++++++++++++++++ include/linux/dma_remapping.h | 22 ++-------------------- 2 files changed, 20 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 26c5402b6f7c..97c36b2ee611 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -150,6 +150,24 @@ struct dma_pte { (p).val |= ((addr) & VTD_PAGE_MASK); } while (0) #define dma_pte_present(p) (((p).val & 3) != 0) +struct dmar_domain { + int id; /* domain id */ + struct intel_iommu *iommu; /* back pointer to owning iommu */ + + struct list_head devices; /* all devices' list */ + struct iova_domain iovad; /* iova's that belong to this domain */ + + struct dma_pte *pgd; /* virtual address */ + spinlock_t mapping_lock; /* page table lock */ + int gaw; /* max guest address width */ + + /* adjusted guest address width, 0 is level 2 30-bit */ + int agaw; + +#define DOMAIN_FLAG_MULTIPLE_DEVICES 1 + int flags; +}; + static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index 9d5874e3bec9..333014468f17 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -9,30 +9,12 @@ #define VTD_PAGE_MASK (((u64)-1) << VTD_PAGE_SHIFT) #define VTD_PAGE_ALIGN(addr) (((addr) + VTD_PAGE_SIZE - 1) & VTD_PAGE_MASK) -struct root_entry; - #define DMA_PTE_READ (1) #define DMA_PTE_WRITE (2) struct intel_iommu; - -struct dmar_domain { - int id; /* domain id */ - struct intel_iommu *iommu; /* back pointer to owning iommu */ - - struct list_head devices; /* all devices' list */ - struct iova_domain iovad; /* iova's that belong to this domain */ - - struct dma_pte *pgd; /* virtual address */ - spinlock_t mapping_lock; /* page table lock */ - int gaw; /* max guest address width */ - - /* adjusted guest address width, 0 is level 2 30-bit */ - int agaw; - -#define DOMAIN_FLAG_MULTIPLE_DEVICES 1 - int flags; -}; +struct dmar_domain; +struct root_entry; /* PCI domain-device relationship */ struct device_domain_info { -- cgit v1.2.3 From a647dacbb1389aa6a5fa631766c1eaea35905890 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:48 +0000 Subject: intel-iommu: move struct device_domain_info out of dma_remapping.h Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 10 ++++++++++ include/linux/dma_remapping.h | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 97c36b2ee611..f23a02054bf7 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -168,6 +168,16 @@ struct dmar_domain { int flags; }; +/* PCI domain-device relationship */ +struct device_domain_info { + struct list_head link; /* link to domain siblings */ + struct list_head global; /* link to global list */ + u8 bus; /* PCI bus numer */ + u8 devfn; /* PCI devfn number */ + struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */ + struct dmar_domain *domain; /* pointer to domain */ +}; + static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index 333014468f17..4ef5f6bc0d68 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -16,16 +16,6 @@ struct intel_iommu; struct dmar_domain; struct root_entry; -/* PCI domain-device relationship */ -struct device_domain_info { - struct list_head link; /* link to domain siblings */ - struct list_head global; /* link to global list */ - u8 bus; /* PCI bus numer */ - u8 devfn; /* PCI devfn number */ - struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */ - struct dmar_domain *domain; /* pointer to domain */ -}; - extern void free_dmar_iommu(struct intel_iommu *iommu); extern int dmar_disabled; -- cgit v1.2.3 From 58fa7304a2c2bfd46e505c293ef779aa1d9715c2 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:49 +0000 Subject: intel-iommu: kill off duplicate def of dmar_disabled This is only used in dmar.c and intel-iommu.h, so dma_remapping.h seems like the appropriate place for it. Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- include/linux/dmar.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/dmar.h b/include/linux/dmar.h index f1984fc3e06d..f28440784cf0 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -144,7 +144,6 @@ struct dmar_rmrr_unit { list_for_each_entry(rmrr, &dmar_rmrr_units, list) /* Intel DMAR initialization functions */ extern int intel_iommu_init(void); -extern int dmar_disabled; #else static inline int intel_iommu_init(void) { -- cgit v1.2.3 From 2abd7e167c1b281f99bb58d302225872bfae9123 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:50 +0000 Subject: intel-iommu: move iommu_prepare_gfx_mapping() out of dma_remapping.h Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 5 +++++ include/linux/dma_remapping.h | 7 ------- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index f23a02054bf7..c1c59a619650 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1686,6 +1686,11 @@ static void __init iommu_prepare_gfx_mapping(void) printk(KERN_ERR "IOMMU: mapping reserved region failed\n"); } } +#else /* !CONFIG_DMAR_GFX_WA */ +static inline void iommu_prepare_gfx_mapping(void) +{ + return; +} #endif #ifdef CONFIG_DMAR_FLOPPY_WA diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index 4ef5f6bc0d68..7799a85614c1 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -20,11 +20,4 @@ extern void free_dmar_iommu(struct intel_iommu *iommu); extern int dmar_disabled; -#ifndef CONFIG_DMAR_GFX_WA -static inline void iommu_prepare_gfx_mapping(void) -{ - return; -} -#endif /* !CONFIG_DMAR_GFX_WA */ - #endif -- cgit v1.2.3 From 1b5736839ae13dadc5947940144f95dd0f4a4a8c Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 15:34:06 +0800 Subject: calculate agaw for each iommu "SAGAW" capability may be different across iommus. Use a default agaw, but if default agaw is not supported in some iommus, choose a less supported agaw. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/dmar.c | 10 ++++++++++ drivers/pci/intel-iommu.c | 22 ++++++++++++++++++++++ include/linux/dma_remapping.h | 1 + include/linux/intel-iommu.h | 1 + 4 files changed, 34 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 5f164ff3026e..f5a662a50acb 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -491,6 +491,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) int map_size; u32 ver; static int iommu_allocated = 0; + int agaw; iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); if (!iommu) @@ -506,6 +507,15 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); + agaw = iommu_calculate_agaw(iommu); + if (agaw < 0) { + printk(KERN_ERR + "Cannot get a valid agaw for iommu (seq_id = %d)\n", + iommu->seq_id); + goto error; + } + iommu->agaw = agaw; + /* the registers might be more than one page */ map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), cap_max_fault_reg_offset(iommu->cap)); diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 9dca689215eb..3ecfa2304c2c 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -362,6 +362,28 @@ void free_iova_mem(struct iova *iova) kmem_cache_free(iommu_iova_cache, iova); } + +static inline int width_to_agaw(int width); + +/* calculate agaw for each iommu. + * "SAGAW" may be different across iommus, use a default agaw, and + * get a supported less agaw for iommus that don't support the default agaw. + */ +int iommu_calculate_agaw(struct intel_iommu *iommu) +{ + unsigned long sagaw; + int agaw = -1; + + sagaw = cap_sagaw(iommu->cap); + for (agaw = width_to_agaw(DEFAULT_DOMAIN_ADDRESS_WIDTH); + agaw >= 0; agaw--) { + if (test_bit(agaw, &sagaw)) + break; + } + + return agaw; +} + /* in native case, each domain is related to only one iommu */ static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain) { diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index 7799a85614c1..136f170cecc2 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -17,6 +17,7 @@ struct dmar_domain; struct root_entry; extern void free_dmar_iommu(struct intel_iommu *iommu); +extern int iommu_calculate_agaw(struct intel_iommu *iommu); extern int dmar_disabled; diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 1bff7bf1bc2c..06349fd5871b 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -290,6 +290,7 @@ struct intel_iommu { u32 gcmd; /* Holds TE, EAFL. Don't need SRTP, SFL, WBF */ spinlock_t register_lock; /* protect register handling */ int seq_id; /* sequence id of the iommu */ + int agaw; /* agaw of this iommu */ #ifdef CONFIG_DMAR unsigned long *domain_ids; /* bitmap of domains */ -- cgit v1.2.3 From faa3d6f5ffe7bf60ebfd0d36513fbcda0eb0ea1a Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 23:09:29 +0800 Subject: Change intel iommu APIs of virtual machine domain These APIs are used by KVM to use VT-d Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 129 ++++++++++++++++++++------------------------ include/linux/intel-iommu.h | 20 +++---- 2 files changed, 70 insertions(+), 79 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 8a204d5bb427..f1380269cabd 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -2944,96 +2944,87 @@ static void vm_domain_exit(struct dmar_domain *domain) free_domain_mem(domain); } -void intel_iommu_domain_exit(struct dmar_domain *domain) +struct dmar_domain *intel_iommu_alloc_domain(void) { - u64 end; - - /* Domain 0 is reserved, so dont process it */ - if (!domain) - return; - - end = DOMAIN_MAX_ADDR(domain->gaw); - end = end & (~VTD_PAGE_MASK); - - /* clear ptes */ - dma_pte_clear_range(domain, 0, end); - - /* free page tables */ - dma_pte_free_pagetable(domain, 0, end); - - iommu_free_domain(domain); - free_domain_mem(domain); -} -EXPORT_SYMBOL_GPL(intel_iommu_domain_exit); - -struct dmar_domain *intel_iommu_domain_alloc(struct pci_dev *pdev) -{ - struct dmar_drhd_unit *drhd; struct dmar_domain *domain; - struct intel_iommu *iommu; - drhd = dmar_find_matched_drhd_unit(pdev); - if (!drhd) { - printk(KERN_ERR "intel_iommu_domain_alloc: drhd == NULL\n"); - return NULL; - } - - iommu = drhd->iommu; - if (!iommu) { - printk(KERN_ERR - "intel_iommu_domain_alloc: iommu == NULL\n"); - return NULL; - } - domain = iommu_alloc_domain(iommu); + domain = iommu_alloc_vm_domain(); if (!domain) { printk(KERN_ERR "intel_iommu_domain_alloc: domain == NULL\n"); return NULL; } - if (domain_init(domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { + if (vm_domain_init(domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { printk(KERN_ERR "intel_iommu_domain_alloc: domain_init() failed\n"); - intel_iommu_domain_exit(domain); + vm_domain_exit(domain); return NULL; } + return domain; } -EXPORT_SYMBOL_GPL(intel_iommu_domain_alloc); +EXPORT_SYMBOL_GPL(intel_iommu_alloc_domain); -int intel_iommu_context_mapping( - struct dmar_domain *domain, struct pci_dev *pdev) +void intel_iommu_free_domain(struct dmar_domain *domain) { - int rc; - rc = domain_context_mapping(domain, pdev); - return rc; + vm_domain_exit(domain); } -EXPORT_SYMBOL_GPL(intel_iommu_context_mapping); +EXPORT_SYMBOL_GPL(intel_iommu_free_domain); -int intel_iommu_page_mapping( - struct dmar_domain *domain, dma_addr_t iova, - u64 hpa, size_t size, int prot) +int intel_iommu_attach_device(struct dmar_domain *domain, + struct pci_dev *pdev) { - int rc; - rc = domain_page_mapping(domain, iova, hpa, size, prot); - return rc; + int ret; + + /* normally pdev is not mapped */ + if (unlikely(domain_context_mapped(pdev))) { + struct dmar_domain *old_domain; + + old_domain = find_domain(pdev); + if (old_domain) { + if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) + vm_domain_remove_one_dev_info(old_domain, pdev); + else + domain_remove_dev_info(old_domain); + } + } + + ret = domain_context_mapping(domain, pdev); + if (ret) + return ret; + + ret = vm_domain_add_dev_info(domain, pdev); + return ret; } -EXPORT_SYMBOL_GPL(intel_iommu_page_mapping); +EXPORT_SYMBOL_GPL(intel_iommu_attach_device); -void intel_iommu_detach_dev(struct dmar_domain *domain, u8 bus, u8 devfn) +void intel_iommu_detach_device(struct dmar_domain *domain, + struct pci_dev *pdev) { - struct intel_iommu *iommu; + vm_domain_remove_one_dev_info(domain, pdev); +} +EXPORT_SYMBOL_GPL(intel_iommu_detach_device); - iommu = device_to_iommu(bus, devfn); - iommu_detach_dev(iommu, bus, devfn); +int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, + u64 hpa, size_t size, int prot) +{ + int ret; + ret = domain_page_mapping(domain, iova, hpa, size, prot); + return ret; } -EXPORT_SYMBOL_GPL(intel_iommu_detach_dev); +EXPORT_SYMBOL_GPL(intel_iommu_map_address); -struct dmar_domain * -intel_iommu_find_domain(struct pci_dev *pdev) +void intel_iommu_unmap_address(struct dmar_domain *domain, + dma_addr_t iova, size_t size) { - return find_domain(pdev); + dma_addr_t base; + + /* The address might not be aligned */ + base = iova & VTD_PAGE_MASK; + size = VTD_PAGE_ALIGN(size); + dma_pte_clear_range(domain, base, base + size); } -EXPORT_SYMBOL_GPL(intel_iommu_find_domain); +EXPORT_SYMBOL_GPL(intel_iommu_unmap_address); int intel_iommu_found(void) { @@ -3041,17 +3032,15 @@ int intel_iommu_found(void) } EXPORT_SYMBOL_GPL(intel_iommu_found); -u64 intel_iommu_iova_to_pfn(struct dmar_domain *domain, u64 iova) +u64 intel_iommu_iova_to_phys(struct dmar_domain *domain, u64 iova) { struct dma_pte *pte; - u64 pfn; + u64 phys = 0; - pfn = 0; pte = addr_to_dma_pte(domain, iova); - if (pte) - pfn = dma_pte_addr(pte); + phys = dma_pte_addr(pte); - return pfn >> VTD_PAGE_SHIFT; + return phys; } -EXPORT_SYMBOL_GPL(intel_iommu_iova_to_pfn); +EXPORT_SYMBOL_GPL(intel_iommu_iova_to_phys); diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 06349fd5871b..07973c4e4acc 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -330,15 +330,17 @@ extern int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); -void intel_iommu_domain_exit(struct dmar_domain *domain); -struct dmar_domain *intel_iommu_domain_alloc(struct pci_dev *pdev); -int intel_iommu_context_mapping(struct dmar_domain *domain, - struct pci_dev *pdev); -int intel_iommu_page_mapping(struct dmar_domain *domain, dma_addr_t iova, - u64 hpa, size_t size, int prot); -void intel_iommu_detach_dev(struct dmar_domain *domain, u8 bus, u8 devfn); -struct dmar_domain *intel_iommu_find_domain(struct pci_dev *pdev); -u64 intel_iommu_iova_to_pfn(struct dmar_domain *domain, u64 iova); +struct dmar_domain *intel_iommu_alloc_domain(void); +void intel_iommu_free_domain(struct dmar_domain *domain); +int intel_iommu_attach_device(struct dmar_domain *domain, + struct pci_dev *pdev); +void intel_iommu_detach_device(struct dmar_domain *domain, + struct pci_dev *pdev); +int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, + u64 hpa, size_t size, int prot); +void intel_iommu_unmap_address(struct dmar_domain *domain, + dma_addr_t iova, size_t size); +u64 intel_iommu_iova_to_phys(struct dmar_domain *domain, u64 iova); #ifdef CONFIG_DMAR int intel_iommu_found(void); -- cgit v1.2.3 From 260782bcfdaaa7850f29d6bb2ec6603019168c57 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Tue, 2 Dec 2008 21:03:39 +0800 Subject: KVM: use the new intel iommu APIs intel iommu APIs are updated, use the new APIs. In addition, change kvm_iommu_map_guest() to just create the domain, let kvm_iommu_assign_device() assign device. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- include/linux/kvm_host.h | 15 +++++--- virt/kvm/kvm_main.c | 7 +++- virt/kvm/vtd.c | 98 +++++++++++++++++++++++++++--------------------- 3 files changed, 71 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index eafabd5c66b2..c96739b4b7a3 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -330,9 +330,10 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); #ifdef CONFIG_DMAR int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn, unsigned long npages); -int kvm_iommu_map_guest(struct kvm *kvm, - struct kvm_assigned_dev_kernel *assigned_dev); +int kvm_iommu_map_guest(struct kvm *kvm); int kvm_iommu_unmap_guest(struct kvm *kvm); +int kvm_assign_device(struct kvm *kvm, + struct kvm_assigned_dev_kernel *assigned_dev); #else /* CONFIG_DMAR */ static inline int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn, @@ -341,9 +342,7 @@ static inline int kvm_iommu_map_pages(struct kvm *kvm, return 0; } -static inline int kvm_iommu_map_guest(struct kvm *kvm, - struct kvm_assigned_dev_kernel - *assigned_dev) +static inline int kvm_iommu_map_guest(struct kvm *kvm) { return -ENODEV; } @@ -352,6 +351,12 @@ static inline int kvm_iommu_unmap_guest(struct kvm *kvm) { return 0; } + +static inline int kvm_assign_device(struct kvm *kvm, + struct kvm_assigned_dev_kernel *assigned_dev) +{ + return 0; +} #endif /* CONFIG_DMAR */ static inline void kvm_guest_enter(void) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index fc6127cbea1f..c92b63462b79 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -503,7 +503,12 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm, list_add(&match->list, &kvm->arch.assigned_dev_head); if (assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) { - r = kvm_iommu_map_guest(kvm, match); + if (!kvm->arch.intel_iommu_domain) { + r = kvm_iommu_map_guest(kvm); + if (r) + goto out_list_del; + } + r = kvm_assign_device(kvm, match); if (r) goto out_list_del; } diff --git a/virt/kvm/vtd.c b/virt/kvm/vtd.c index a770874f3a3a..44bb58a395a5 100644 --- a/virt/kvm/vtd.c +++ b/virt/kvm/vtd.c @@ -45,20 +45,18 @@ int kvm_iommu_map_pages(struct kvm *kvm, for (i = 0; i < npages; i++) { /* check if already mapped */ - pfn = (pfn_t)intel_iommu_iova_to_pfn(domain, - gfn_to_gpa(gfn)); - if (pfn) + if (intel_iommu_iova_to_phys(domain, + gfn_to_gpa(gfn))) continue; pfn = gfn_to_pfn(kvm, gfn); - r = intel_iommu_page_mapping(domain, - gfn_to_gpa(gfn), - pfn_to_hpa(pfn), - PAGE_SIZE, - DMA_PTE_READ | - DMA_PTE_WRITE); + r = intel_iommu_map_address(domain, + gfn_to_gpa(gfn), + pfn_to_hpa(pfn), + PAGE_SIZE, + DMA_PTE_READ | DMA_PTE_WRITE); if (r) { - printk(KERN_ERR "kvm_iommu_map_pages:" + printk(KERN_ERR "kvm_iommu_map_address:" "iommu failed to map pfn=%lx\n", pfn); goto unmap_pages; } @@ -86,50 +84,55 @@ static int kvm_iommu_map_memslots(struct kvm *kvm) return r; } -int kvm_iommu_map_guest(struct kvm *kvm, - struct kvm_assigned_dev_kernel *assigned_dev) +int kvm_assign_device(struct kvm *kvm, + struct kvm_assigned_dev_kernel *assigned_dev) { struct pci_dev *pdev = NULL; + struct dmar_domain *domain = kvm->arch.intel_iommu_domain; int r; - if (!intel_iommu_found()) { - printk(KERN_ERR "%s: intel iommu not found\n", __func__); + /* check if iommu exists and in use */ + if (!domain) + return 0; + + pdev = assigned_dev->dev; + if (pdev == NULL) return -ENODEV; + + r = intel_iommu_attach_device(domain, pdev); + if (r) { + printk(KERN_ERR "assign device %x:%x.%x failed", + pdev->bus->number, + PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); + return r; } - printk(KERN_DEBUG "VT-d direct map: host bdf = %x:%x:%x\n", - assigned_dev->host_busnr, - PCI_SLOT(assigned_dev->host_devfn), - PCI_FUNC(assigned_dev->host_devfn)); + printk(KERN_DEBUG "assign device: host bdf = %x:%x:%x\n", + assigned_dev->host_busnr, + PCI_SLOT(assigned_dev->host_devfn), + PCI_FUNC(assigned_dev->host_devfn)); - pdev = assigned_dev->dev; + return 0; +} - if (pdev == NULL) { - if (kvm->arch.intel_iommu_domain) { - intel_iommu_domain_exit(kvm->arch.intel_iommu_domain); - kvm->arch.intel_iommu_domain = NULL; - } +int kvm_iommu_map_guest(struct kvm *kvm) +{ + int r; + + if (!intel_iommu_found()) { + printk(KERN_ERR "%s: intel iommu not found\n", __func__); return -ENODEV; } - kvm->arch.intel_iommu_domain = intel_iommu_domain_alloc(pdev); + kvm->arch.intel_iommu_domain = intel_iommu_alloc_domain(); if (!kvm->arch.intel_iommu_domain) - return -ENODEV; + return -ENOMEM; r = kvm_iommu_map_memslots(kvm); if (r) goto out_unmap; - intel_iommu_detach_dev(kvm->arch.intel_iommu_domain, - pdev->bus->number, pdev->devfn); - - r = intel_iommu_context_mapping(kvm->arch.intel_iommu_domain, - pdev); - if (r) { - printk(KERN_ERR "Domain context map for %s failed", - pci_name(pdev)); - goto out_unmap; - } return 0; out_unmap: @@ -138,19 +141,29 @@ out_unmap: } static void kvm_iommu_put_pages(struct kvm *kvm, - gfn_t base_gfn, unsigned long npages) + gfn_t base_gfn, unsigned long npages) { gfn_t gfn = base_gfn; pfn_t pfn; struct dmar_domain *domain = kvm->arch.intel_iommu_domain; - int i; + unsigned long i; + u64 phys; + + /* check if iommu exists and in use */ + if (!domain) + return; for (i = 0; i < npages; i++) { - pfn = (pfn_t)intel_iommu_iova_to_pfn(domain, - gfn_to_gpa(gfn)); + phys = intel_iommu_iova_to_phys(domain, + gfn_to_gpa(gfn)); + pfn = phys >> PAGE_SHIFT; kvm_release_pfn_clean(pfn); gfn++; } + + intel_iommu_unmap_address(domain, + gfn_to_gpa(base_gfn), + PAGE_SIZE * npages); } static int kvm_iommu_unmap_memslots(struct kvm *kvm) @@ -182,10 +195,9 @@ int kvm_iommu_unmap_guest(struct kvm *kvm) PCI_FUNC(entry->host_devfn)); /* detach kvm dmar domain */ - intel_iommu_detach_dev(domain, entry->host_busnr, - entry->host_devfn); + intel_iommu_detach_device(domain, entry->dev); } kvm_iommu_unmap_memslots(kvm); - intel_iommu_domain_exit(domain); + intel_iommu_free_domain(domain); return 0; } -- cgit v1.2.3 From 0a920356748df4fb06e86c21c23d2ed6d31d37ad Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Tue, 2 Dec 2008 21:24:23 +0800 Subject: KVM: support device deassignment Support device deassignment, it can be used in device hotplug. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- include/linux/kvm_host.h | 8 ++++++++ virt/kvm/kvm_main.c | 42 ++++++++++++++++++++++++++++++++++++++++++ virt/kvm/vtd.c | 24 ++++++++++++++++++++++++ 3 files changed, 74 insertions(+) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c96739b4b7a3..ce5d1c17ce26 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -334,6 +334,8 @@ int kvm_iommu_map_guest(struct kvm *kvm); int kvm_iommu_unmap_guest(struct kvm *kvm); int kvm_assign_device(struct kvm *kvm, struct kvm_assigned_dev_kernel *assigned_dev); +int kvm_deassign_device(struct kvm *kvm, + struct kvm_assigned_dev_kernel *assigned_dev); #else /* CONFIG_DMAR */ static inline int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn, @@ -357,6 +359,12 @@ static inline int kvm_assign_device(struct kvm *kvm, { return 0; } + +static inline int kvm_deassign_device(struct kvm *kvm, + struct kvm_assigned_dev_kernel *assigned_dev) +{ + return 0; +} #endif /* CONFIG_DMAR */ static inline void kvm_guest_enter(void) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index c92b63462b79..3238e08e4651 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -530,6 +530,35 @@ out_free: } #endif +#ifdef KVM_CAP_DEVICE_DEASSIGNMENT +static int kvm_vm_ioctl_deassign_device(struct kvm *kvm, + struct kvm_assigned_pci_dev *assigned_dev) +{ + int r = 0; + struct kvm_assigned_dev_kernel *match; + + mutex_lock(&kvm->lock); + + match = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head, + assigned_dev->assigned_dev_id); + if (!match) { + printk(KERN_INFO "%s: device hasn't been assigned before, " + "so cannot be deassigned\n", __func__); + r = -EINVAL; + goto out; + } + + if (assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) + kvm_deassign_device(kvm, match); + + kvm_free_assigned_device(kvm, match); + +out: + mutex_unlock(&kvm->lock); + return r; +} +#endif + static inline int valid_vcpu(int n) { return likely(n >= 0 && n < KVM_MAX_VCPUS); @@ -1862,6 +1891,19 @@ static long kvm_vm_ioctl(struct file *filp, goto out; break; } +#endif +#ifdef KVM_CAP_DEVICE_DEASSIGNMENT + case KVM_DEASSIGN_PCI_DEVICE: { + struct kvm_assigned_pci_dev assigned_dev; + + r = -EFAULT; + if (copy_from_user(&assigned_dev, argp, sizeof assigned_dev)) + goto out; + r = kvm_vm_ioctl_deassign_device(kvm, &assigned_dev); + if (r) + goto out; + break; + } #endif default: r = kvm_arch_vm_ioctl(filp, ioctl, arg); diff --git a/virt/kvm/vtd.c b/virt/kvm/vtd.c index 44bb58a395a5..174ea1f8cee5 100644 --- a/virt/kvm/vtd.c +++ b/virt/kvm/vtd.c @@ -116,6 +116,30 @@ int kvm_assign_device(struct kvm *kvm, return 0; } +int kvm_deassign_device(struct kvm *kvm, + struct kvm_assigned_dev_kernel *assigned_dev) +{ + struct dmar_domain *domain = kvm->arch.intel_iommu_domain; + struct pci_dev *pdev = NULL; + + /* check if iommu exists and in use */ + if (!domain) + return 0; + + pdev = assigned_dev->dev; + if (pdev == NULL) + return -ENODEV; + + intel_iommu_detach_device(domain, pdev); + + printk(KERN_DEBUG "deassign device: host bdf = %x:%x:%x\n", + assigned_dev->host_busnr, + PCI_SLOT(assigned_dev->host_devfn), + PCI_FUNC(assigned_dev->host_devfn)); + + return 0; +} + int kvm_iommu_map_guest(struct kvm *kvm) { int r; -- cgit v1.2.3 From b653574a7d14b663cc812cb20be6a114939ba186 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 23:29:53 +0800 Subject: Deassign device in kvm_free_assgined_device In kvm_iommu_unmap_memslots(), assigned_dev_head is already empty. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 1 + virt/kvm/vtd.c | 10 ---------- 3 files changed, 2 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index ce5d1c17ce26..e62a4629e51c 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -316,6 +316,7 @@ struct kvm_assigned_dev_kernel { #define KVM_ASSIGNED_DEV_HOST_MSI (1 << 9) unsigned long irq_requested_type; int irq_source_id; + int flags; struct pci_dev *dev; struct kvm *kvm; }; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 3238e08e4651..4ef0fb43d1f9 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -496,6 +496,7 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm, match->assigned_dev_id = assigned_dev->assigned_dev_id; match->host_busnr = assigned_dev->busnr; match->host_devfn = assigned_dev->devfn; + match->flags = assigned_dev->flags; match->dev = dev; match->irq_source_id = -1; match->kvm = kvm; diff --git a/virt/kvm/vtd.c b/virt/kvm/vtd.c index 174ea1f8cee5..d46de9af838b 100644 --- a/virt/kvm/vtd.c +++ b/virt/kvm/vtd.c @@ -205,22 +205,12 @@ static int kvm_iommu_unmap_memslots(struct kvm *kvm) int kvm_iommu_unmap_guest(struct kvm *kvm) { - struct kvm_assigned_dev_kernel *entry; struct dmar_domain *domain = kvm->arch.intel_iommu_domain; /* check if iommu exists and in use */ if (!domain) return 0; - list_for_each_entry(entry, &kvm->arch.assigned_dev_head, list) { - printk(KERN_DEBUG "VT-d unmap: host bdf = %x:%x:%x\n", - entry->host_busnr, - PCI_SLOT(entry->host_devfn), - PCI_FUNC(entry->host_devfn)); - - /* detach kvm dmar domain */ - intel_iommu_detach_device(domain, entry->dev); - } kvm_iommu_unmap_memslots(kvm); intel_iommu_free_domain(domain); return 0; -- cgit v1.2.3 From 4a77a6cf6d9bf9f5c74b27f62bd2bfe6dcc88392 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 26 Nov 2008 17:02:33 +0100 Subject: introcude linux/iommu.h for an iommu api This patch introduces the API to abstract the exported VT-d functions for KVM into a generic API. This way the AMD IOMMU implementation can plug into this API later. Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 include/linux/iommu.h (limited to 'include/linux') diff --git a/include/linux/iommu.h b/include/linux/iommu.h new file mode 100644 index 000000000000..8a7bfb1b6ca0 --- /dev/null +++ b/include/linux/iommu.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2007-2008 Advanced Micro Devices, Inc. + * Author: Joerg Roedel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_IOMMU_H +#define __LINUX_IOMMU_H + +#define IOMMU_READ (1) +#define IOMMU_WRITE (2) + +struct device; + +struct iommu_domain { + void *priv; +}; + +struct iommu_ops { + int (*domain_init)(struct iommu_domain *domain); + void (*domain_destroy)(struct iommu_domain *domain); + int (*attach_dev)(struct iommu_domain *domain, struct device *dev); + void (*detach_dev)(struct iommu_domain *domain, struct device *dev); + int (*map)(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot); + void (*unmap)(struct iommu_domain *domain, unsigned long iova, + size_t size); + phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, + unsigned long iova); +}; + +#ifdef CONFIG_IOMMU_API + +extern void register_iommu(struct iommu_ops *ops); +extern bool iommu_found(void); +extern struct iommu_domain *iommu_domain_alloc(void); +extern void iommu_domain_free(struct iommu_domain *domain); +extern int iommu_attach_device(struct iommu_domain *domain, + struct device *dev); +extern void iommu_detach_device(struct iommu_domain *domain, + struct device *dev); +extern int iommu_map_range(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot); +extern void iommu_unmap_range(struct iommu_domain *domain, unsigned long iova, + size_t size); +extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova); + +#else /* CONFIG_IOMMU_API */ + +static inline void register_iommu(struct iommu_ops *ops) +{ +} + +static inline bool iommu_found(void) +{ + return false; +} + +static inline struct iommu_domain *iommu_domain_alloc(void) +{ + return NULL; +} + +static inline void iommu_domain_free(struct iommu_domain *domain) +{ +} + +static inline int iommu_attach_device(struct iommu_domain *domain, + struct device *dev) +{ + return -ENODEV; +} + +static inline void iommu_detach_device(struct iommu_domain *domain, + struct device *dev) +{ +} + +static inline int iommu_map_range(struct iommu_domain *domain, + unsigned long iova, phys_addr_t paddr, + size_t size, int prot) +{ + return -ENODEV; +} + +static inline void iommu_unmap_range(struct iommu_domain *domain, + unsigned long iova, size_t size) +{ +} + +static inline phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova) +{ + return 0; +} + +#endif /* CONFIG_IOMMU_API */ + +#endif /* __LINUX_IOMMU_H */ -- cgit v1.2.3 From 19de40a8472fa64693eab844911eec277d489f6c Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 14:43:34 +0100 Subject: KVM: change KVM to use IOMMU API Signed-off-by: Joerg Roedel --- arch/ia64/include/asm/kvm_host.h | 2 +- arch/ia64/kvm/Makefile | 2 +- arch/ia64/kvm/kvm-ia64.c | 3 ++- arch/x86/include/asm/kvm_host.h | 2 +- arch/x86/kvm/Makefile | 2 +- arch/x86/kvm/x86.c | 3 ++- include/linux/kvm_host.h | 6 +++--- virt/kvm/iommu.c | 45 +++++++++++++++++++--------------------- virt/kvm/kvm_main.c | 2 +- 9 files changed, 33 insertions(+), 34 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/include/asm/kvm_host.h b/arch/ia64/include/asm/kvm_host.h index 0560f3fae538..348663661659 100644 --- a/arch/ia64/include/asm/kvm_host.h +++ b/arch/ia64/include/asm/kvm_host.h @@ -467,7 +467,7 @@ struct kvm_arch { struct kvm_sal_data rdv_sal_data; struct list_head assigned_dev_head; - struct dmar_domain *intel_iommu_domain; + struct iommu_domain *iommu_domain; struct hlist_head irq_ack_notifier_list; unsigned long irq_sources_bitmap; diff --git a/arch/ia64/kvm/Makefile b/arch/ia64/kvm/Makefile index cb69dfcfb1a6..0bb99b732908 100644 --- a/arch/ia64/kvm/Makefile +++ b/arch/ia64/kvm/Makefile @@ -51,7 +51,7 @@ EXTRA_AFLAGS += -Ivirt/kvm -Iarch/ia64/kvm/ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \ coalesced_mmio.o irq_comm.o) -ifeq ($(CONFIG_DMAR),y) +ifeq ($(CONFIG_IOMMU_API),y) common-objs += $(addprefix ../../../virt/kvm/, iommu.o) endif diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 0f5ebd948437..4e586f6110aa 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -188,7 +189,7 @@ int kvm_dev_ioctl_check_extension(long ext) r = KVM_COALESCED_MMIO_PAGE_OFFSET; break; case KVM_CAP_IOMMU: - r = intel_iommu_found(); + r = iommu_found(); break; default: r = 0; diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 97215a458e5f..730843d1d2fb 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -360,7 +360,7 @@ struct kvm_arch{ struct list_head active_mmu_pages; struct list_head assigned_dev_head; struct list_head oos_global_pages; - struct dmar_domain *intel_iommu_domain; + struct iommu_domain *iommu_domain; struct kvm_pic *vpic; struct kvm_ioapic *vioapic; struct kvm_pit *vpit; diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index 00f46c2d14bd..d3ec292f00f2 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -7,7 +7,7 @@ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \ ifeq ($(CONFIG_KVM_TRACE),y) common-objs += $(addprefix ../../../virt/kvm/, kvm_trace.o) endif -ifeq ($(CONFIG_DMAR),y) +ifeq ($(CONFIG_IOMMU_API),y) common-objs += $(addprefix ../../../virt/kvm/, iommu.o) endif diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0e6aa8141dcd..cc17546a2406 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -989,7 +990,7 @@ int kvm_dev_ioctl_check_extension(long ext) r = !tdp_enabled; break; case KVM_CAP_IOMMU: - r = intel_iommu_found(); + r = iommu_found(); break; default: r = 0; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index e62a4629e51c..ec49d0be7f52 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -328,7 +328,7 @@ void kvm_unregister_irq_ack_notifier(struct kvm_irq_ack_notifier *kian); int kvm_request_irq_source_id(struct kvm *kvm); void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); -#ifdef CONFIG_DMAR +#ifdef CONFIG_IOMMU_API int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn, unsigned long npages); int kvm_iommu_map_guest(struct kvm *kvm); @@ -337,7 +337,7 @@ int kvm_assign_device(struct kvm *kvm, struct kvm_assigned_dev_kernel *assigned_dev); int kvm_deassign_device(struct kvm *kvm, struct kvm_assigned_dev_kernel *assigned_dev); -#else /* CONFIG_DMAR */ +#else /* CONFIG_IOMMU_API */ static inline int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn, unsigned long npages) @@ -366,7 +366,7 @@ static inline int kvm_deassign_device(struct kvm *kvm, { return 0; } -#endif /* CONFIG_DMAR */ +#endif /* CONFIG_IOMMU_API */ static inline void kvm_guest_enter(void) { diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c index d46de9af838b..d0bebaa5bf0a 100644 --- a/virt/kvm/iommu.c +++ b/virt/kvm/iommu.c @@ -25,6 +25,7 @@ #include #include #include +#include #include static int kvm_iommu_unmap_memslots(struct kvm *kvm); @@ -37,7 +38,7 @@ int kvm_iommu_map_pages(struct kvm *kvm, gfn_t gfn = base_gfn; pfn_t pfn; int i, r = 0; - struct dmar_domain *domain = kvm->arch.intel_iommu_domain; + struct iommu_domain *domain = kvm->arch.iommu_domain; /* check if iommu exists and in use */ if (!domain) @@ -45,16 +46,15 @@ int kvm_iommu_map_pages(struct kvm *kvm, for (i = 0; i < npages; i++) { /* check if already mapped */ - if (intel_iommu_iova_to_phys(domain, - gfn_to_gpa(gfn))) + if (iommu_iova_to_phys(domain, gfn_to_gpa(gfn))) continue; pfn = gfn_to_pfn(kvm, gfn); - r = intel_iommu_map_address(domain, - gfn_to_gpa(gfn), - pfn_to_hpa(pfn), - PAGE_SIZE, - DMA_PTE_READ | DMA_PTE_WRITE); + r = iommu_map_range(domain, + gfn_to_gpa(gfn), + pfn_to_hpa(pfn), + PAGE_SIZE, + IOMMU_READ | IOMMU_WRITE); if (r) { printk(KERN_ERR "kvm_iommu_map_address:" "iommu failed to map pfn=%lx\n", pfn); @@ -88,7 +88,7 @@ int kvm_assign_device(struct kvm *kvm, struct kvm_assigned_dev_kernel *assigned_dev) { struct pci_dev *pdev = NULL; - struct dmar_domain *domain = kvm->arch.intel_iommu_domain; + struct iommu_domain *domain = kvm->arch.iommu_domain; int r; /* check if iommu exists and in use */ @@ -99,7 +99,7 @@ int kvm_assign_device(struct kvm *kvm, if (pdev == NULL) return -ENODEV; - r = intel_iommu_attach_device(domain, pdev); + r = iommu_attach_device(domain, &pdev->dev); if (r) { printk(KERN_ERR "assign device %x:%x.%x failed", pdev->bus->number, @@ -119,7 +119,7 @@ int kvm_assign_device(struct kvm *kvm, int kvm_deassign_device(struct kvm *kvm, struct kvm_assigned_dev_kernel *assigned_dev) { - struct dmar_domain *domain = kvm->arch.intel_iommu_domain; + struct iommu_domain *domain = kvm->arch.iommu_domain; struct pci_dev *pdev = NULL; /* check if iommu exists and in use */ @@ -130,7 +130,7 @@ int kvm_deassign_device(struct kvm *kvm, if (pdev == NULL) return -ENODEV; - intel_iommu_detach_device(domain, pdev); + iommu_detach_device(domain, &pdev->dev); printk(KERN_DEBUG "deassign device: host bdf = %x:%x:%x\n", assigned_dev->host_busnr, @@ -144,13 +144,13 @@ int kvm_iommu_map_guest(struct kvm *kvm) { int r; - if (!intel_iommu_found()) { - printk(KERN_ERR "%s: intel iommu not found\n", __func__); + if (!iommu_found()) { + printk(KERN_ERR "%s: iommu not found\n", __func__); return -ENODEV; } - kvm->arch.intel_iommu_domain = intel_iommu_alloc_domain(); - if (!kvm->arch.intel_iommu_domain) + kvm->arch.iommu_domain = iommu_domain_alloc(); + if (!kvm->arch.iommu_domain) return -ENOMEM; r = kvm_iommu_map_memslots(kvm); @@ -169,7 +169,7 @@ static void kvm_iommu_put_pages(struct kvm *kvm, { gfn_t gfn = base_gfn; pfn_t pfn; - struct dmar_domain *domain = kvm->arch.intel_iommu_domain; + struct iommu_domain *domain = kvm->arch.iommu_domain; unsigned long i; u64 phys; @@ -178,16 +178,13 @@ static void kvm_iommu_put_pages(struct kvm *kvm, return; for (i = 0; i < npages; i++) { - phys = intel_iommu_iova_to_phys(domain, - gfn_to_gpa(gfn)); + phys = iommu_iova_to_phys(domain, gfn_to_gpa(gfn)); pfn = phys >> PAGE_SHIFT; kvm_release_pfn_clean(pfn); gfn++; } - intel_iommu_unmap_address(domain, - gfn_to_gpa(base_gfn), - PAGE_SIZE * npages); + iommu_unmap_range(domain, gfn_to_gpa(base_gfn), PAGE_SIZE * npages); } static int kvm_iommu_unmap_memslots(struct kvm *kvm) @@ -205,13 +202,13 @@ static int kvm_iommu_unmap_memslots(struct kvm *kvm) int kvm_iommu_unmap_guest(struct kvm *kvm) { - struct dmar_domain *domain = kvm->arch.intel_iommu_domain; + struct iommu_domain *domain = kvm->arch.iommu_domain; /* check if iommu exists and in use */ if (!domain) return 0; kvm_iommu_unmap_memslots(kvm); - intel_iommu_free_domain(domain); + iommu_domain_free(domain); return 0; } diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 4ef0fb43d1f9..3a5a08298aab 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -504,7 +504,7 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm, list_add(&match->list, &kvm->arch.assigned_dev_head); if (assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) { - if (!kvm->arch.intel_iommu_domain) { + if (!kvm->arch.iommu_domain) { r = kvm_iommu_map_guest(kvm); if (r) goto out_list_del; -- cgit v1.2.3 From 5d450806eb0e569c5846a5825e7f535980b0da32 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 14:52:32 +0100 Subject: VT-d: adapt domain init and destroy functions for IOMMU API Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 33 ++++++++++++++++++--------------- include/linux/intel-iommu.h | 2 -- 2 files changed, 18 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 772fb22e1be0..5c95a5a65440 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -2962,32 +2963,34 @@ static void vm_domain_exit(struct dmar_domain *domain) free_domain_mem(domain); } -struct dmar_domain *intel_iommu_alloc_domain(void) +static int intel_iommu_domain_init(struct iommu_domain *domain) { - struct dmar_domain *domain; + struct dmar_domain *dmar_domain; - domain = iommu_alloc_vm_domain(); - if (!domain) { + dmar_domain = iommu_alloc_vm_domain(); + if (!dmar_domain) { printk(KERN_ERR - "intel_iommu_domain_alloc: domain == NULL\n"); - return NULL; + "intel_iommu_domain_init: dmar_domain == NULL\n"); + return -ENOMEM; } - if (vm_domain_init(domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { + if (vm_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { printk(KERN_ERR - "intel_iommu_domain_alloc: domain_init() failed\n"); - vm_domain_exit(domain); - return NULL; + "intel_iommu_domain_init() failed\n"); + vm_domain_exit(dmar_domain); + return -ENOMEM; } + domain->priv = dmar_domain; - return domain; + return 0; } -EXPORT_SYMBOL_GPL(intel_iommu_alloc_domain); -void intel_iommu_free_domain(struct dmar_domain *domain) +static void intel_iommu_domain_destroy(struct iommu_domain *domain) { - vm_domain_exit(domain); + struct dmar_domain *dmar_domain = domain->priv; + + domain->priv = NULL; + vm_domain_exit(dmar_domain); } -EXPORT_SYMBOL_GPL(intel_iommu_free_domain); int intel_iommu_attach_device(struct dmar_domain *domain, struct pci_dev *pdev) diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 07973c4e4acc..0a7ba0cefc74 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -330,8 +330,6 @@ extern int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); -struct dmar_domain *intel_iommu_alloc_domain(void); -void intel_iommu_free_domain(struct dmar_domain *domain); int intel_iommu_attach_device(struct dmar_domain *domain, struct pci_dev *pdev); void intel_iommu_detach_device(struct dmar_domain *domain, -- cgit v1.2.3 From 4c5478c94eb29e6101f1f13175f7455bc8b5d953 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 14:58:24 +0100 Subject: VT-d: adapt device attach and detach functions for IOMMU API Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 27 +++++++++++++++------------ include/linux/intel-iommu.h | 4 ---- 2 files changed, 15 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 5c95a5a65440..db9a26cfeb8f 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -2992,9 +2992,11 @@ static void intel_iommu_domain_destroy(struct iommu_domain *domain) vm_domain_exit(dmar_domain); } -int intel_iommu_attach_device(struct dmar_domain *domain, - struct pci_dev *pdev) +static int intel_iommu_attach_device(struct iommu_domain *domain, + struct device *dev) { + struct dmar_domain *dmar_domain = domain->priv; + struct pci_dev *pdev = to_pci_dev(dev); struct intel_iommu *iommu; int addr_width; u64 end; @@ -3006,7 +3008,7 @@ int intel_iommu_attach_device(struct dmar_domain *domain, old_domain = find_domain(pdev); if (old_domain) { - if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) + if (dmar_domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) vm_domain_remove_one_dev_info(old_domain, pdev); else domain_remove_dev_info(old_domain); @@ -3021,28 +3023,29 @@ int intel_iommu_attach_device(struct dmar_domain *domain, addr_width = agaw_to_width(iommu->agaw); end = DOMAIN_MAX_ADDR(addr_width); end = end & VTD_PAGE_MASK; - if (end < domain->max_addr) { + if (end < dmar_domain->max_addr) { printk(KERN_ERR "%s: iommu agaw (%d) is not " "sufficient for the mapped address (%llx)\n", - __func__, iommu->agaw, domain->max_addr); + __func__, iommu->agaw, dmar_domain->max_addr); return -EFAULT; } - ret = domain_context_mapping(domain, pdev); + ret = domain_context_mapping(dmar_domain, pdev); if (ret) return ret; - ret = vm_domain_add_dev_info(domain, pdev); + ret = vm_domain_add_dev_info(dmar_domain, pdev); return ret; } -EXPORT_SYMBOL_GPL(intel_iommu_attach_device); -void intel_iommu_detach_device(struct dmar_domain *domain, - struct pci_dev *pdev) +static void intel_iommu_detach_device(struct iommu_domain *domain, + struct device *dev) { - vm_domain_remove_one_dev_info(domain, pdev); + struct dmar_domain *dmar_domain = domain->priv; + struct pci_dev *pdev = to_pci_dev(dev); + + vm_domain_remove_one_dev_info(dmar_domain, pdev); } -EXPORT_SYMBOL_GPL(intel_iommu_detach_device); int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, u64 hpa, size_t size, int prot) diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 0a7ba0cefc74..9909c5a1b20f 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -330,10 +330,6 @@ extern int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); -int intel_iommu_attach_device(struct dmar_domain *domain, - struct pci_dev *pdev); -void intel_iommu_detach_device(struct dmar_domain *domain, - struct pci_dev *pdev); int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, u64 hpa, size_t size, int prot); void intel_iommu_unmap_address(struct dmar_domain *domain, -- cgit v1.2.3 From dde57a210dcdce85e2813bab8f88687761d9f6a6 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 15:04:09 +0100 Subject: VT-d: adapt domain map and unmap functions for IOMMU API Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 33 ++++++++++++++++++++------------- include/linux/intel-iommu.h | 4 ---- 2 files changed, 20 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index db9a26cfeb8f..8af6c96f31b3 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3047,20 +3047,28 @@ static void intel_iommu_detach_device(struct iommu_domain *domain, vm_domain_remove_one_dev_info(dmar_domain, pdev); } -int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, - u64 hpa, size_t size, int prot) +static int intel_iommu_map_range(struct iommu_domain *domain, + unsigned long iova, phys_addr_t hpa, + size_t size, int iommu_prot) { + struct dmar_domain *dmar_domain = domain->priv; u64 max_addr; int addr_width; + int prot = 0; int ret; + if (iommu_prot & IOMMU_READ) + prot |= DMA_PTE_READ; + if (iommu_prot & IOMMU_WRITE) + prot |= DMA_PTE_WRITE; + max_addr = (iova & VTD_PAGE_MASK) + VTD_PAGE_ALIGN(size); - if (domain->max_addr < max_addr) { + if (dmar_domain->max_addr < max_addr) { int min_agaw; u64 end; /* check if minimum agaw is sufficient for mapped address */ - min_agaw = vm_domain_min_agaw(domain); + min_agaw = vm_domain_min_agaw(dmar_domain); addr_width = agaw_to_width(min_agaw); end = DOMAIN_MAX_ADDR(addr_width); end = end & VTD_PAGE_MASK; @@ -3070,28 +3078,27 @@ int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, __func__, min_agaw, max_addr); return -EFAULT; } - domain->max_addr = max_addr; + dmar_domain->max_addr = max_addr; } - ret = domain_page_mapping(domain, iova, hpa, size, prot); + ret = domain_page_mapping(dmar_domain, iova, hpa, size, prot); return ret; } -EXPORT_SYMBOL_GPL(intel_iommu_map_address); -void intel_iommu_unmap_address(struct dmar_domain *domain, - dma_addr_t iova, size_t size) +static void intel_iommu_unmap_range(struct iommu_domain *domain, + unsigned long iova, size_t size) { + struct dmar_domain *dmar_domain = domain->priv; dma_addr_t base; /* The address might not be aligned */ base = iova & VTD_PAGE_MASK; size = VTD_PAGE_ALIGN(size); - dma_pte_clear_range(domain, base, base + size); + dma_pte_clear_range(dmar_domain, base, base + size); - if (domain->max_addr == base + size) - domain->max_addr = base; + if (dmar_domain->max_addr == base + size) + dmar_domain->max_addr = base; } -EXPORT_SYMBOL_GPL(intel_iommu_unmap_address); int intel_iommu_found(void) { diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 9909c5a1b20f..6bc26e03858c 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -330,10 +330,6 @@ extern int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); -int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, - u64 hpa, size_t size, int prot); -void intel_iommu_unmap_address(struct dmar_domain *domain, - dma_addr_t iova, size_t size); u64 intel_iommu_iova_to_phys(struct dmar_domain *domain, u64 iova); #ifdef CONFIG_DMAR -- cgit v1.2.3 From d14d65777c2491dd5baf1e17f444b8f653f3cbb1 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 15:06:57 +0100 Subject: VT-d: adapt domain iova_to_phys function for IOMMU API Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 7 ++++--- include/linux/intel-iommu.h | 2 -- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 8af6c96f31b3..712810598a2e 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3106,15 +3106,16 @@ int intel_iommu_found(void) } EXPORT_SYMBOL_GPL(intel_iommu_found); -u64 intel_iommu_iova_to_phys(struct dmar_domain *domain, u64 iova) +static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova) { + struct dmar_domain *dmar_domain = domain->priv; struct dma_pte *pte; u64 phys = 0; - pte = addr_to_dma_pte(domain, iova); + pte = addr_to_dma_pte(dmar_domain, iova); if (pte) phys = dma_pte_addr(pte); return phys; } -EXPORT_SYMBOL_GPL(intel_iommu_iova_to_phys); diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 6bc26e03858c..26ccc0294567 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -330,8 +330,6 @@ extern int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); -u64 intel_iommu_iova_to_phys(struct dmar_domain *domain, u64 iova); - #ifdef CONFIG_DMAR int intel_iommu_found(void); #else /* CONFIG_DMAR */ -- cgit v1.2.3 From e4754c96cf8b82a754dc5ba791d6c0bf1fbe8e8e Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 15:26:42 +0100 Subject: VT-d: remove now unused intel_iommu_found function Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 6 ------ include/linux/intel-iommu.h | 9 --------- 2 files changed, 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 81e04ec85d97..ecb5fd3b71f7 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3105,12 +3105,6 @@ static void intel_iommu_unmap_range(struct iommu_domain *domain, dmar_domain->max_addr = base; } -int intel_iommu_found(void) -{ - return g_num_of_iommus; -} -EXPORT_SYMBOL_GPL(intel_iommu_found); - static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) { diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 26ccc0294567..c4f6c101dbcd 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -330,15 +330,6 @@ extern int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); -#ifdef CONFIG_DMAR -int intel_iommu_found(void); -#else /* CONFIG_DMAR */ -static inline int intel_iommu_found(void) -{ - return 0; -} -#endif /* CONFIG_DMAR */ - extern void *intel_alloc_coherent(struct device *, size_t, dma_addr_t *, gfp_t); extern void intel_free_coherent(struct device *, size_t, void *, dma_addr_t); extern dma_addr_t intel_map_single(struct device *, phys_addr_t, size_t, int); -- cgit v1.2.3 From 2f983570010a0dcb26d988da02d7ccfad00c807c Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Jan 2009 00:06:34 -0800 Subject: sparseirq: move set/get_timer_rand_state back to .c those two functions only used in that C file Signed-off-by: Yinghai Lu Signed-off-by: Linus Torvalds --- drivers/char/random.c | 40 ++++++++++++++++++++++++++++++++++------ include/linux/random.h | 50 -------------------------------------------------- 2 files changed, 34 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/random.c b/drivers/char/random.c index d26891bfcd41..c7afc068c28d 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -559,7 +559,40 @@ struct timer_rand_state { }; #ifndef CONFIG_SPARSE_IRQ -struct timer_rand_state *irq_timer_state[NR_IRQS]; + +static struct timer_rand_state *irq_timer_state[NR_IRQS]; + +static struct timer_rand_state *get_timer_rand_state(unsigned int irq) +{ + return irq_timer_state[irq]; +} + +static void set_timer_rand_state(unsigned int irq, + struct timer_rand_state *state) +{ + irq_timer_state[irq] = state; +} + +#else + +static struct timer_rand_state *get_timer_rand_state(unsigned int irq) +{ + struct irq_desc *desc; + + desc = irq_to_desc(irq); + + return desc->timer_rand_state; +} + +static void set_timer_rand_state(unsigned int irq, + struct timer_rand_state *state) +{ + struct irq_desc *desc; + + desc = irq_to_desc(irq); + + desc->timer_rand_state = state; +} #endif static struct timer_rand_state input_timer_state; @@ -919,11 +952,6 @@ void rand_initialize_irq(int irq) { struct timer_rand_state *state; -#ifndef CONFIG_SPARSE_IRQ - if (irq >= nr_irqs) - return; -#endif - state = get_timer_rand_state(irq); if (state) diff --git a/include/linux/random.h b/include/linux/random.h index adbf3bd3c6b3..407ea3646f8f 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -45,56 +45,6 @@ struct rand_pool_info { extern void rand_initialize_irq(int irq); -struct timer_rand_state; -#ifndef CONFIG_SPARSE_IRQ - -extern struct timer_rand_state *irq_timer_state[]; - -static inline struct timer_rand_state *get_timer_rand_state(unsigned int irq) -{ - if (irq >= nr_irqs) - return NULL; - - return irq_timer_state[irq]; -} - -static inline void set_timer_rand_state(unsigned int irq, struct timer_rand_state *state) -{ - if (irq >= nr_irqs) - return; - - irq_timer_state[irq] = state; -} - -#else - -#include -static inline struct timer_rand_state *get_timer_rand_state(unsigned int irq) -{ - struct irq_desc *desc; - - desc = irq_to_desc(irq); - - if (!desc) - return NULL; - - return desc->timer_rand_state; -} - -static inline void set_timer_rand_state(unsigned int irq, struct timer_rand_state *state) -{ - struct irq_desc *desc; - - desc = irq_to_desc(irq); - - if (!desc) - return; - - desc->timer_rand_state = state; -} -#endif - - extern void add_input_randomness(unsigned int type, unsigned int code, unsigned int value); extern void add_interrupt_randomness(int irq); -- cgit v1.2.3 From f3298dc4f2277874d40cb4fc3a6e277317d6603b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 10 Dec 2008 03:16:51 -0500 Subject: sanitize audit_socketcall * don't bother with allocations * now that it can't fail, make it return void Signed-off-by: Al Viro --- include/linux/audit.h | 4 ++-- kernel/auditsc.c | 66 +++++++++++++++++++++++++++++---------------------- net/socket.c | 4 +--- 3 files changed, 41 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index 26c4f6f65a46..466a953d4bf6 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -446,7 +446,7 @@ extern void audit_log_task_context(struct audit_buffer *ab); extern int __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); extern int audit_bprm(struct linux_binprm *bprm); -extern int audit_socketcall(int nargs, unsigned long *args); +extern void audit_socketcall(int nargs, unsigned long *args); extern int audit_sockaddr(int len, void *addr); extern int __audit_fd_pair(int fd1, int fd2); extern int audit_set_macxattr(const char *name); @@ -549,7 +549,7 @@ extern int audit_signals; #define audit_ipc_obj(i) ({ 0; }) #define audit_ipc_set_perm(q,u,g,m) ({ 0; }) #define audit_bprm(p) ({ 0; }) -#define audit_socketcall(n,a) ({ 0; }) +#define audit_socketcall(n,a) ((void)0) #define audit_fd_pair(n,a) ({ 0; }) #define audit_sockaddr(len, addr) ({ 0; }) #define audit_set_macxattr(n) do { ; } while (0) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index c2e43ebb1b68..5cda66466e14 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -168,12 +168,6 @@ struct audit_aux_data_execve { struct mm_struct *mm; }; -struct audit_aux_data_socketcall { - struct audit_aux_data d; - int nargs; - unsigned long args[0]; -}; - struct audit_aux_data_fd_pair { struct audit_aux_data d; int fd[2]; @@ -247,6 +241,14 @@ struct audit_context { struct audit_tree_refs *trees, *first_trees; int tree_count; + int type; + union { + struct { + int nargs; + long args[6]; + } socketcall; + }; + #if AUDIT_DEBUG int put_count; int ino_count; @@ -1226,6 +1228,27 @@ static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name) audit_log_format(ab, " cap_fe=%d cap_fver=%x", name->fcap.fE, name->fcap_ver); } +static void show_special(struct audit_context *context) +{ + struct audit_buffer *ab; + int i; + + ab = audit_log_start(context, GFP_KERNEL, context->type); + if (!ab) + return; + + switch (context->type) { + case AUDIT_SOCKETCALL: { + int nargs = context->socketcall.nargs; + audit_log_format(ab, "nargs=%d", nargs); + for (i = 0; i < nargs; i++) + audit_log_format(ab, " a%d=%lx", i, + context->socketcall.args[i]); + break; } + } + audit_log_end(ab); +} + static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) { const struct cred *cred; @@ -1372,13 +1395,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts audit_log_execve_info(context, &ab, axi); break; } - case AUDIT_SOCKETCALL: { - struct audit_aux_data_socketcall *axs = (void *)aux; - audit_log_format(ab, "nargs=%d", axs->nargs); - for (i=0; inargs; i++) - audit_log_format(ab, " a%d=%lx", i, axs->args[i]); - break; } - case AUDIT_FD_PAIR: { struct audit_aux_data_fd_pair *axs = (void *)aux; audit_log_format(ab, "fd0=%d fd1=%d", axs->fd[0], axs->fd[1]); @@ -1410,6 +1426,9 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts audit_log_end(ab); } + if (context->type) + show_special(context); + if (context->sockaddr_len) { ab = audit_log_start(context, GFP_KERNEL, AUDIT_SOCKADDR); if (ab) { @@ -1689,6 +1708,7 @@ void audit_syscall_exit(int valid, long return_code) context->target_pid = 0; context->target_sid = 0; context->sockaddr_len = 0; + context->type = 0; kfree(context->filterkey); context->filterkey = NULL; tsk->audit_context = context; @@ -2406,27 +2426,17 @@ int audit_bprm(struct linux_binprm *bprm) * @nargs: number of args * @args: args array * - * Returns 0 for success or NULL context or < 0 on error. */ -int audit_socketcall(int nargs, unsigned long *args) +void audit_socketcall(int nargs, unsigned long *args) { - struct audit_aux_data_socketcall *ax; struct audit_context *context = current->audit_context; if (likely(!context || context->dummy)) - return 0; - - ax = kmalloc(sizeof(*ax) + nargs * sizeof(unsigned long), GFP_KERNEL); - if (!ax) - return -ENOMEM; - - ax->nargs = nargs; - memcpy(ax->args, args, nargs * sizeof(unsigned long)); + return; - ax->d.type = AUDIT_SOCKETCALL; - ax->d.next = context->aux; - context->aux = (void *)ax; - return 0; + context->type = AUDIT_SOCKETCALL; + context->socketcall.nargs = nargs; + memcpy(context->socketcall.args, args, nargs * sizeof(unsigned long)); } /** diff --git a/net/socket.c b/net/socket.c index 2c730fc718ab..b41a92093e40 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2065,9 +2065,7 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) if (copy_from_user(a, args, nargs[call])) return -EFAULT; - err = audit_socketcall(nargs[call] / sizeof(unsigned long), a); - if (err) - return err; + audit_socketcall(nargs[call] / sizeof(unsigned long), a); a0 = a[0]; a1 = a[1]; -- cgit v1.2.3 From a33e6751003c5ade603737d828b1519d980ce392 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 10 Dec 2008 03:40:06 -0500 Subject: sanitize audit_ipc_obj() * get rid of allocations * make it return void * simplify callers Signed-off-by: Al Viro --- include/linux/audit.h | 9 +++--- ipc/shm.c | 4 +-- ipc/util.c | 9 ++---- kernel/auditsc.c | 88 ++++++++++++++++++++++----------------------------- 4 files changed, 45 insertions(+), 65 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index 466a953d4bf6..f8578b9088e1 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -443,7 +443,7 @@ extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); #define audit_get_loginuid(t) ((t)->loginuid) #define audit_get_sessionid(t) ((t)->sessionid) extern void audit_log_task_context(struct audit_buffer *ab); -extern int __audit_ipc_obj(struct kern_ipc_perm *ipcp); +extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); extern int audit_bprm(struct linux_binprm *bprm); extern void audit_socketcall(int nargs, unsigned long *args); @@ -460,11 +460,10 @@ extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, const struct cred *old); extern int __audit_log_capset(pid_t pid, const struct cred *new, const struct cred *old); -static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp) +static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp) { if (unlikely(!audit_dummy_context())) - return __audit_ipc_obj(ipcp); - return 0; + __audit_ipc_obj(ipcp); } static inline int audit_fd_pair(int fd1, int fd2) { @@ -546,7 +545,7 @@ extern int audit_signals; #define audit_get_loginuid(t) (-1) #define audit_get_sessionid(t) (-1) #define audit_log_task_context(b) do { ; } while (0) -#define audit_ipc_obj(i) ({ 0; }) +#define audit_ipc_obj(i) ((void)0) #define audit_ipc_set_perm(q,u,g,m) ({ 0; }) #define audit_bprm(p) ({ 0; }) #define audit_socketcall(n,a) ((void)0) diff --git a/ipc/shm.c b/ipc/shm.c index 38a055758a9b..57dd50046cef 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -747,9 +747,7 @@ asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf) goto out; } - err = audit_ipc_obj(&(shp->shm_perm)); - if (err) - goto out_unlock; + audit_ipc_obj(&(shp->shm_perm)); if (!capable(CAP_IPC_LOCK)) { uid_t euid = current_euid(); diff --git a/ipc/util.c b/ipc/util.c index 5a1808c774a2..579552abd50a 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -624,10 +624,9 @@ void ipc_rcu_putref(void *ptr) int ipcperms (struct kern_ipc_perm *ipcp, short flag) { /* flag will most probably be 0 or S_...UGO from */ uid_t euid = current_euid(); - int requested_mode, granted_mode, err; + int requested_mode, granted_mode; - if (unlikely((err = audit_ipc_obj(ipcp)))) - return err; + audit_ipc_obj(ipcp); requested_mode = (flag >> 6) | (flag >> 3) | flag; granted_mode = ipcp->mode; if (euid == ipcp->cuid || @@ -803,9 +802,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, goto out_up; } - err = audit_ipc_obj(ipcp); - if (err) - goto out_unlock; + audit_ipc_obj(ipcp); if (cmd == IPC_SET) { err = audit_ipc_set_perm(extra_perm, perm->uid, diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 5cda66466e14..73504313264f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -247,6 +247,12 @@ struct audit_context { int nargs; long args[6]; } socketcall; + struct { + uid_t uid; + gid_t gid; + mode_t mode; + u32 osid; + } ipc; }; #if AUDIT_DEBUG @@ -605,19 +611,12 @@ static int audit_filter_rules(struct task_struct *tsk, } } /* Find ipc objects that match */ - if (ctx) { - struct audit_aux_data *aux; - for (aux = ctx->aux; aux; - aux = aux->next) { - if (aux->type == AUDIT_IPC) { - struct audit_aux_data_ipcctl *axi = (void *)aux; - if (security_audit_rule_match(axi->osid, f->type, f->op, f->lsm_rule, ctx)) { - ++result; - break; - } - } - } - } + if (!ctx || ctx->type != AUDIT_IPC) + break; + if (security_audit_rule_match(ctx->ipc.osid, + f->type, f->op, + f->lsm_rule, ctx)) + ++result; } break; case AUDIT_ARG0: @@ -1228,7 +1227,7 @@ static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name) audit_log_format(ab, " cap_fe=%d cap_fver=%x", name->fcap.fE, name->fcap_ver); } -static void show_special(struct audit_context *context) +static void show_special(struct audit_context *context, int *call_panic) { struct audit_buffer *ab; int i; @@ -1245,6 +1244,23 @@ static void show_special(struct audit_context *context) audit_log_format(ab, " a%d=%lx", i, context->socketcall.args[i]); break; } + case AUDIT_IPC: { + u32 osid = context->ipc.osid; + + audit_log_format(ab, "ouid=%u ogid=%u mode=%#o", + context->ipc.uid, context->ipc.gid, context->ipc.mode); + if (osid) { + char *ctx = NULL; + u32 len; + if (security_secid_to_secctx(osid, &ctx, &len)) { + audit_log_format(ab, " osid=%u", osid); + *call_panic = 1; + } else { + audit_log_format(ab, " obj=%s", ctx); + security_release_secctx(ctx, len); + } + } + break; } } audit_log_end(ab); } @@ -1363,26 +1379,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts axi->mqstat.mq_msgsize, axi->mqstat.mq_curmsgs); break; } - case AUDIT_IPC: { - struct audit_aux_data_ipcctl *axi = (void *)aux; - audit_log_format(ab, - "ouid=%u ogid=%u mode=%#o", - axi->uid, axi->gid, axi->mode); - if (axi->osid != 0) { - char *ctx = NULL; - u32 len; - if (security_secid_to_secctx( - axi->osid, &ctx, &len)) { - audit_log_format(ab, " osid=%u", - axi->osid); - call_panic = 1; - } else { - audit_log_format(ab, " obj=%s", ctx); - security_release_secctx(ctx, len); - } - } - break; } - case AUDIT_IPC_SET_PERM: { struct audit_aux_data_ipcctl *axi = (void *)aux; audit_log_format(ab, @@ -1427,7 +1423,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts } if (context->type) - show_special(context); + show_special(context, &call_panic); if (context->sockaddr_len) { ab = audit_log_start(context, GFP_KERNEL, AUDIT_SOCKADDR); @@ -2349,25 +2345,15 @@ int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat) * audit_ipc_obj - record audit data for ipc object * @ipcp: ipc permissions * - * Returns 0 for success or NULL context or < 0 on error. */ -int __audit_ipc_obj(struct kern_ipc_perm *ipcp) +void __audit_ipc_obj(struct kern_ipc_perm *ipcp) { - struct audit_aux_data_ipcctl *ax; struct audit_context *context = current->audit_context; - - ax = kmalloc(sizeof(*ax), GFP_ATOMIC); - if (!ax) - return -ENOMEM; - - ax->uid = ipcp->uid; - ax->gid = ipcp->gid; - ax->mode = ipcp->mode; - security_ipc_getsecid(ipcp, &ax->osid); - ax->d.type = AUDIT_IPC; - ax->d.next = context->aux; - context->aux = (void *)ax; - return 0; + context->ipc.uid = ipcp->uid; + context->ipc.gid = ipcp->gid; + context->ipc.mode = ipcp->mode; + security_ipc_getsecid(ipcp, &context->ipc.osid); + context->type = AUDIT_IPC; } /** -- cgit v1.2.3 From e816f370cbadd2afea9f1a42f232d0636137d563 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 10 Dec 2008 03:47:15 -0500 Subject: sanitize audit_ipc_set_perm() * get rid of allocations * make it return void * simplify callers Signed-off-by: Al Viro --- include/linux/audit.h | 9 ++++---- ipc/util.c | 9 ++------ kernel/auditsc.c | 59 +++++++++++++++++++++++---------------------------- 3 files changed, 32 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index f8578b9088e1..b7abfe0d6737 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -444,7 +444,7 @@ extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); #define audit_get_sessionid(t) ((t)->sessionid) extern void audit_log_task_context(struct audit_buffer *ab); extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp); -extern int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); +extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); extern int audit_bprm(struct linux_binprm *bprm); extern void audit_socketcall(int nargs, unsigned long *args); extern int audit_sockaddr(int len, void *addr); @@ -471,11 +471,10 @@ static inline int audit_fd_pair(int fd1, int fd2) return __audit_fd_pair(fd1, fd2); return 0; } -static inline int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) +static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) { if (unlikely(!audit_dummy_context())) - return __audit_ipc_set_perm(qbytes, uid, gid, mode); - return 0; + __audit_ipc_set_perm(qbytes, uid, gid, mode); } static inline int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr) { @@ -546,7 +545,7 @@ extern int audit_signals; #define audit_get_sessionid(t) (-1) #define audit_log_task_context(b) do { ; } while (0) #define audit_ipc_obj(i) ((void)0) -#define audit_ipc_set_perm(q,u,g,m) ({ 0; }) +#define audit_ipc_set_perm(q,u,g,m) ((void)0) #define audit_bprm(p) ({ 0; }) #define audit_socketcall(n,a) ((void)0) #define audit_fd_pair(n,a) ({ 0; }) diff --git a/ipc/util.c b/ipc/util.c index 579552abd50a..7585a72e259b 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -803,13 +803,9 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, } audit_ipc_obj(ipcp); - - if (cmd == IPC_SET) { - err = audit_ipc_set_perm(extra_perm, perm->uid, + if (cmd == IPC_SET) + audit_ipc_set_perm(extra_perm, perm->uid, perm->gid, perm->mode); - if (err) - goto out_unlock; - } euid = current_euid(); if (euid == ipcp->cuid || @@ -817,7 +813,6 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, return ipcp; err = -EPERM; -out_unlock: ipc_unlock(ipcp); out_up: up_write(&ids->rw_mutex); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 73504313264f..fbed62e05bce 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -151,16 +151,6 @@ struct audit_aux_data_mq_getsetattr { struct mq_attr mqstat; }; -struct audit_aux_data_ipcctl { - struct audit_aux_data d; - struct ipc_perm p; - unsigned long qbytes; - uid_t uid; - gid_t gid; - mode_t mode; - u32 osid; -}; - struct audit_aux_data_execve { struct audit_aux_data d; int argc; @@ -252,6 +242,11 @@ struct audit_context { gid_t gid; mode_t mode; u32 osid; + int has_perm; + uid_t perm_uid; + gid_t perm_gid; + mode_t perm_mode; + unsigned long qbytes; } ipc; }; @@ -1260,6 +1255,19 @@ static void show_special(struct audit_context *context, int *call_panic) security_release_secctx(ctx, len); } } + if (context->ipc.has_perm) { + audit_log_end(ab); + ab = audit_log_start(context, GFP_KERNEL, + AUDIT_IPC_SET_PERM); + audit_log_format(ab, + "qbytes=%lx ouid=%u ogid=%u mode=%#o", + context->ipc.qbytes, + context->ipc.perm_uid, + context->ipc.perm_gid, + context->ipc.perm_mode); + if (!ab) + return; + } break; } } audit_log_end(ab); @@ -1379,13 +1387,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts axi->mqstat.mq_msgsize, axi->mqstat.mq_curmsgs); break; } - case AUDIT_IPC_SET_PERM: { - struct audit_aux_data_ipcctl *axi = (void *)aux; - audit_log_format(ab, - "qbytes=%lx ouid=%u ogid=%u mode=%#o", - axi->qbytes, axi->uid, axi->gid, axi->mode); - break; } - case AUDIT_EXECVE: { struct audit_aux_data_execve *axi = (void *)aux; audit_log_execve_info(context, &ab, axi); @@ -2352,6 +2353,7 @@ void __audit_ipc_obj(struct kern_ipc_perm *ipcp) context->ipc.uid = ipcp->uid; context->ipc.gid = ipcp->gid; context->ipc.mode = ipcp->mode; + context->ipc.has_perm = 0; security_ipc_getsecid(ipcp, &context->ipc.osid); context->type = AUDIT_IPC; } @@ -2363,26 +2365,17 @@ void __audit_ipc_obj(struct kern_ipc_perm *ipcp) * @gid: msgq group id * @mode: msgq mode (permissions) * - * Returns 0 for success or NULL context or < 0 on error. + * Called only after audit_ipc_obj(). */ -int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) +void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) { - struct audit_aux_data_ipcctl *ax; struct audit_context *context = current->audit_context; - ax = kmalloc(sizeof(*ax), GFP_ATOMIC); - if (!ax) - return -ENOMEM; - - ax->qbytes = qbytes; - ax->uid = uid; - ax->gid = gid; - ax->mode = mode; - - ax->d.type = AUDIT_IPC_SET_PERM; - ax->d.next = context->aux; - context->aux = (void *)ax; - return 0; + context->ipc.qbytes = qbytes; + context->ipc.perm_uid = uid; + context->ipc.perm_gid = gid; + context->ipc.perm_mode = mode; + context->ipc.has_perm = 1; } int audit_bprm(struct linux_binprm *bprm) -- cgit v1.2.3 From 7392906ea915b9a2c14dea32b3604b4e178f82f7 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 10 Dec 2008 06:58:59 -0500 Subject: sanitize audit_mq_getsetattr() * get rid of allocations * make it return void * don't duplicate parts of audit_dummy_context() Signed-off-by: Al Viro --- include/linux/audit.h | 9 ++++----- ipc/mqueue.c | 6 +----- kernel/auditsc.c | 54 ++++++++++++++++----------------------------------- 3 files changed, 22 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index b7abfe0d6737..b7707e577b80 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -454,7 +454,7 @@ extern int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout); extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout); extern int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification); -extern int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat); +extern void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat); extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, const struct cred *new, const struct cred *old); @@ -500,11 +500,10 @@ static inline int audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_n return __audit_mq_notify(mqdes, u_notification); return 0; } -static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat) +static inline void audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat) { if (unlikely(!audit_dummy_context())) - return __audit_mq_getsetattr(mqdes, mqstat); - return 0; + __audit_mq_getsetattr(mqdes, mqstat); } static inline int audit_log_bprm_fcaps(struct linux_binprm *bprm, @@ -555,7 +554,7 @@ extern int audit_signals; #define audit_mq_timedsend(d,l,p,t) ({ 0; }) #define audit_mq_timedreceive(d,l,p,t) ({ 0; }) #define audit_mq_notify(d,n) ({ 0; }) -#define audit_mq_getsetattr(d,s) ({ 0; }) +#define audit_mq_getsetattr(d,s) ((void)0) #define audit_log_bprm_fcaps(b, ncr, ocr) ({ 0; }) #define audit_log_capset(pid, ncr, ocr) ({ 0; }) #define audit_ptrace(t) ((void)0) diff --git a/ipc/mqueue.c b/ipc/mqueue.c index d9393f8e4c3e..7563611c6615 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -1150,11 +1150,7 @@ asmlinkage long sys_mq_getsetattr(mqd_t mqdes, omqstat = info->attr; omqstat.mq_flags = filp->f_flags & O_NONBLOCK; if (u_mqstat) { - ret = audit_mq_getsetattr(mqdes, &mqstat); - if (ret != 0) { - spin_unlock(&info->lock); - goto out_fput; - } + audit_mq_getsetattr(mqdes, &mqstat); if (mqstat.mq_flags & O_NONBLOCK) filp->f_flags |= O_NONBLOCK; else diff --git a/kernel/auditsc.c b/kernel/auditsc.c index fbed62e05bce..c50178c7e245 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -145,12 +145,6 @@ struct audit_aux_data_mq_notify { struct sigevent notification; }; -struct audit_aux_data_mq_getsetattr { - struct audit_aux_data d; - mqd_t mqdes; - struct mq_attr mqstat; -}; - struct audit_aux_data_execve { struct audit_aux_data d; int argc; @@ -248,6 +242,10 @@ struct audit_context { mode_t perm_mode; unsigned long qbytes; } ipc; + struct { + mqd_t mqdes; + struct mq_attr mqstat; + } mq_getsetattr; }; #if AUDIT_DEBUG @@ -1269,6 +1267,15 @@ static void show_special(struct audit_context *context, int *call_panic) return; } break; } + case AUDIT_MQ_GETSETATTR: { + struct mq_attr *attr = &context->mq_getsetattr.mqstat; + audit_log_format(ab, + "mqdes=%d mq_flags=0x%lx mq_maxmsg=%ld mq_msgsize=%ld " + "mq_curmsgs=%ld ", + context->mq_getsetattr.mqdes, + attr->mq_flags, attr->mq_maxmsg, + attr->mq_msgsize, attr->mq_curmsgs); + break; } } audit_log_end(ab); } @@ -1377,16 +1384,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts axi->notification.sigev_signo); break; } - case AUDIT_MQ_GETSETATTR: { - struct audit_aux_data_mq_getsetattr *axi = (void *)aux; - audit_log_format(ab, - "mqdes=%d mq_flags=0x%lx mq_maxmsg=%ld mq_msgsize=%ld " - "mq_curmsgs=%ld ", - axi->mqdes, - axi->mqstat.mq_flags, axi->mqstat.mq_maxmsg, - axi->mqstat.mq_msgsize, axi->mqstat.mq_curmsgs); - break; } - case AUDIT_EXECVE: { struct audit_aux_data_execve *axi = (void *)aux; audit_log_execve_info(context, &ab, axi); @@ -2316,30 +2313,13 @@ int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification) * @mqdes: MQ descriptor * @mqstat: MQ flags * - * Returns 0 for success or NULL context or < 0 on error. */ -int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat) +void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat) { - struct audit_aux_data_mq_getsetattr *ax; struct audit_context *context = current->audit_context; - - if (!audit_enabled) - return 0; - - if (likely(!context)) - return 0; - - ax = kmalloc(sizeof(*ax), GFP_ATOMIC); - if (!ax) - return -ENOMEM; - - ax->mqdes = mqdes; - ax->mqstat = *mqstat; - - ax->d.type = AUDIT_MQ_GETSETATTR; - ax->d.next = context->aux; - context->aux = (void *)ax; - return 0; + context->mq_getsetattr.mqdes = mqdes; + context->mq_getsetattr.mqstat = *mqstat; + context->type = AUDIT_MQ_GETSETATTR; } /** -- cgit v1.2.3 From 20114f71b27cafeb7c7e41d2b0f0b68c3fbb022b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 10 Dec 2008 07:16:12 -0500 Subject: sanitize audit_mq_notify() * don't copy_from_user() twice * don't bother with allocations * don't duplicate parts of audit_dummy_context() * make it return void Signed-off-by: Al Viro --- include/linux/audit.h | 9 ++++----- ipc/mqueue.c | 14 ++++++------- kernel/auditsc.c | 56 +++++++++++++++------------------------------------ 3 files changed, 27 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index b7707e577b80..8101d2c4a995 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -453,7 +453,7 @@ extern int audit_set_macxattr(const char *name); extern int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr); extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout); extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout); -extern int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification); +extern void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification); extern void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat); extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, const struct cred *new, @@ -494,11 +494,10 @@ static inline int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned in return __audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout); return 0; } -static inline int audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification) +static inline void audit_mq_notify(mqd_t mqdes, const struct sigevent *notification) { if (unlikely(!audit_dummy_context())) - return __audit_mq_notify(mqdes, u_notification); - return 0; + __audit_mq_notify(mqdes, notification); } static inline void audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat) { @@ -553,7 +552,7 @@ extern int audit_signals; #define audit_mq_open(o,m,a) ({ 0; }) #define audit_mq_timedsend(d,l,p,t) ({ 0; }) #define audit_mq_timedreceive(d,l,p,t) ({ 0; }) -#define audit_mq_notify(d,n) ({ 0; }) +#define audit_mq_notify(d,n) ((void)0) #define audit_mq_getsetattr(d,s) ((void)0) #define audit_log_bprm_fcaps(b, ncr, ocr) ({ 0; }) #define audit_log_capset(pid, ncr, ocr) ({ 0; }) diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 7563611c6615..e7b2f68f8d77 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -1003,17 +1003,17 @@ asmlinkage long sys_mq_notify(mqd_t mqdes, struct mqueue_inode_info *info; struct sk_buff *nc; - ret = audit_mq_notify(mqdes, u_notification); - if (ret != 0) - return ret; - - nc = NULL; - sock = NULL; - if (u_notification != NULL) { + if (u_notification) { if (copy_from_user(¬ification, u_notification, sizeof(struct sigevent))) return -EFAULT; + } + + audit_mq_notify(mqdes, u_notification ? ¬ification : NULL); + nc = NULL; + sock = NULL; + if (u_notification != NULL) { if (unlikely(notification.sigev_notify != SIGEV_NONE && notification.sigev_notify != SIGEV_SIGNAL && notification.sigev_notify != SIGEV_THREAD)) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index c50178c7e245..3ece960de894 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -139,12 +139,6 @@ struct audit_aux_data_mq_sendrecv { struct timespec abs_timeout; }; -struct audit_aux_data_mq_notify { - struct audit_aux_data d; - mqd_t mqdes; - struct sigevent notification; -}; - struct audit_aux_data_execve { struct audit_aux_data d; int argc; @@ -246,6 +240,10 @@ struct audit_context { mqd_t mqdes; struct mq_attr mqstat; } mq_getsetattr; + struct { + mqd_t mqdes; + int sigev_signo; + } mq_notify; }; #if AUDIT_DEBUG @@ -1267,6 +1265,11 @@ static void show_special(struct audit_context *context, int *call_panic) return; } break; } + case AUDIT_MQ_NOTIFY: { + audit_log_format(ab, "mqdes=%d sigev_signo=%d", + context->mq_notify.mqdes, + context->mq_notify.sigev_signo); + break; } case AUDIT_MQ_GETSETATTR: { struct mq_attr *attr = &context->mq_getsetattr.mqstat; audit_log_format(ab, @@ -1376,14 +1379,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts axi->abs_timeout.tv_sec, axi->abs_timeout.tv_nsec); break; } - case AUDIT_MQ_NOTIFY: { - struct audit_aux_data_mq_notify *axi = (void *)aux; - audit_log_format(ab, - "mqdes=%d sigev_signo=%d", - axi->mqdes, - axi->notification.sigev_signo); - break; } - case AUDIT_EXECVE: { struct audit_aux_data_execve *axi = (void *)aux; audit_log_execve_info(context, &ab, axi); @@ -2274,38 +2269,19 @@ int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, * @mqdes: MQ descriptor * @u_notification: Notification event * - * Returns 0 for success or NULL context or < 0 on error. */ -int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification) +void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification) { - struct audit_aux_data_mq_notify *ax; struct audit_context *context = current->audit_context; - if (!audit_enabled) - return 0; - - if (likely(!context)) - return 0; - - ax = kmalloc(sizeof(*ax), GFP_ATOMIC); - if (!ax) - return -ENOMEM; - - if (u_notification != NULL) { - if (copy_from_user(&ax->notification, u_notification, sizeof(ax->notification))) { - kfree(ax); - return -EFAULT; - } - } else - memset(&ax->notification, 0, sizeof(ax->notification)); - - ax->mqdes = mqdes; + if (notification) + context->mq_notify.sigev_signo = notification->sigev_signo; + else + context->mq_notify.sigev_signo = 0; - ax->d.type = AUDIT_MQ_NOTIFY; - ax->d.next = context->aux; - context->aux = (void *)ax; - return 0; + context->mq_notify.mqdes = mqdes; + context->type = AUDIT_MQ_NOTIFY; } /** -- cgit v1.2.3 From c32c8af43b9adde8d6f938d8e6328c13b8de79ac Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 14 Dec 2008 03:46:48 -0500 Subject: sanitize AUDIT_MQ_SENDRECV * logging the original value of *msg_prio in mq_timedreceive(2) is insane - the argument is write-only (i.e. syscall always ignores the original value and only overwrites it). * merge __audit_mq_timed{send,receive} * don't do copy_from_user() twice * don't mess with allocations in auditsc part * ... and don't bother checking !audit_enabled and !context in there - we'd already checked for audit_dummy_context(). Signed-off-by: Al Viro --- include/linux/audit.h | 17 ++----- ipc/mqueue.c | 54 +++++++++++---------- kernel/auditsc.c | 127 ++++++++++++-------------------------------------- 3 files changed, 63 insertions(+), 135 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index 8101d2c4a995..67f0cdd991ba 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -451,8 +451,7 @@ extern int audit_sockaddr(int len, void *addr); extern int __audit_fd_pair(int fd1, int fd2); extern int audit_set_macxattr(const char *name); extern int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr); -extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout); -extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout); +extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout); extern void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification); extern void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat); extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, @@ -482,17 +481,10 @@ static inline int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u return __audit_mq_open(oflag, mode, u_attr); return 0; } -static inline int audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout) +static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout) { if (unlikely(!audit_dummy_context())) - return __audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout); - return 0; -} -static inline int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout) -{ - if (unlikely(!audit_dummy_context())) - return __audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout); - return 0; + __audit_mq_sendrecv(mqdes, msg_len, msg_prio, abs_timeout); } static inline void audit_mq_notify(mqd_t mqdes, const struct sigevent *notification) { @@ -550,8 +542,7 @@ extern int audit_signals; #define audit_sockaddr(len, addr) ({ 0; }) #define audit_set_macxattr(n) do { ; } while (0) #define audit_mq_open(o,m,a) ({ 0; }) -#define audit_mq_timedsend(d,l,p,t) ({ 0; }) -#define audit_mq_timedreceive(d,l,p,t) ({ 0; }) +#define audit_mq_sendrecv(d,l,p,t) ((void)0) #define audit_mq_notify(d,n) ((void)0) #define audit_mq_getsetattr(d,s) ((void)0) #define audit_log_bprm_fcaps(b, ncr, ocr) ({ 0; }) diff --git a/ipc/mqueue.c b/ipc/mqueue.c index e7b2f68f8d77..192da806c283 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -524,31 +524,27 @@ static void __do_notify(struct mqueue_inode_info *info) wake_up(&info->wait_q); } -static long prepare_timeout(const struct timespec __user *u_arg) +static long prepare_timeout(struct timespec *p) { - struct timespec ts, nowts; + struct timespec nowts; long timeout; - if (u_arg) { - if (unlikely(copy_from_user(&ts, u_arg, - sizeof(struct timespec)))) - return -EFAULT; - - if (unlikely(ts.tv_nsec < 0 || ts.tv_sec < 0 - || ts.tv_nsec >= NSEC_PER_SEC)) + if (p) { + if (unlikely(p->tv_nsec < 0 || p->tv_sec < 0 + || p->tv_nsec >= NSEC_PER_SEC)) return -EINVAL; nowts = CURRENT_TIME; /* first subtract as jiffies can't be too big */ - ts.tv_sec -= nowts.tv_sec; - if (ts.tv_nsec < nowts.tv_nsec) { - ts.tv_nsec += NSEC_PER_SEC; - ts.tv_sec--; + p->tv_sec -= nowts.tv_sec; + if (p->tv_nsec < nowts.tv_nsec) { + p->tv_nsec += NSEC_PER_SEC; + p->tv_sec--; } - ts.tv_nsec -= nowts.tv_nsec; - if (ts.tv_sec < 0) + p->tv_nsec -= nowts.tv_nsec; + if (p->tv_sec < 0) return 0; - timeout = timespec_to_jiffies(&ts) + 1; + timeout = timespec_to_jiffies(p) + 1; } else return MAX_SCHEDULE_TIMEOUT; @@ -829,17 +825,22 @@ asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr, struct ext_wait_queue *receiver; struct msg_msg *msg_ptr; struct mqueue_inode_info *info; + struct timespec ts, *p = NULL; long timeout; int ret; - ret = audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout); - if (ret != 0) - return ret; + if (u_abs_timeout) { + if (copy_from_user(&ts, u_abs_timeout, + sizeof(struct timespec))) + return -EFAULT; + p = &ts; + } if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) return -EINVAL; - timeout = prepare_timeout(u_abs_timeout); + audit_mq_sendrecv(mqdes, msg_len, msg_prio, p); + timeout = prepare_timeout(p); ret = -EBADF; filp = fget(mqdes); @@ -918,12 +919,17 @@ asmlinkage ssize_t sys_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr, struct inode *inode; struct mqueue_inode_info *info; struct ext_wait_queue wait; + struct timespec ts, *p = NULL; - ret = audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout); - if (ret != 0) - return ret; + if (u_abs_timeout) { + if (copy_from_user(&ts, u_abs_timeout, + sizeof(struct timespec))) + return -EFAULT; + p = &ts; + } - timeout = prepare_timeout(u_abs_timeout); + audit_mq_sendrecv(mqdes, msg_len, 0, p); + timeout = prepare_timeout(p); ret = -EBADF; filp = fget(mqdes); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 3ece960de894..140c47453470 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -131,14 +131,6 @@ struct audit_aux_data_mq_open { struct mq_attr attr; }; -struct audit_aux_data_mq_sendrecv { - struct audit_aux_data d; - mqd_t mqdes; - size_t msg_len; - unsigned int msg_prio; - struct timespec abs_timeout; -}; - struct audit_aux_data_execve { struct audit_aux_data d; int argc; @@ -244,6 +236,12 @@ struct audit_context { mqd_t mqdes; int sigev_signo; } mq_notify; + struct { + mqd_t mqdes; + size_t msg_len; + unsigned int msg_prio; + struct timespec abs_timeout; + } mq_sendrecv; }; #if AUDIT_DEBUG @@ -1265,6 +1263,16 @@ static void show_special(struct audit_context *context, int *call_panic) return; } break; } + case AUDIT_MQ_SENDRECV: { + audit_log_format(ab, + "mqdes=%d msg_len=%zd msg_prio=%u " + "abs_timeout_sec=%ld abs_timeout_nsec=%ld", + context->mq_sendrecv.mqdes, + context->mq_sendrecv.msg_len, + context->mq_sendrecv.msg_prio, + context->mq_sendrecv.abs_timeout.tv_sec, + context->mq_sendrecv.abs_timeout.tv_nsec); + break; } case AUDIT_MQ_NOTIFY: { audit_log_format(ab, "mqdes=%d sigev_signo=%d", context->mq_notify.mqdes, @@ -1370,15 +1378,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts axi->attr.mq_curmsgs); break; } - case AUDIT_MQ_SENDRECV: { - struct audit_aux_data_mq_sendrecv *axi = (void *)aux; - audit_log_format(ab, - "mqdes=%d msg_len=%zd msg_prio=%u " - "abs_timeout_sec=%ld abs_timeout_nsec=%ld", - axi->mqdes, axi->msg_len, axi->msg_prio, - axi->abs_timeout.tv_sec, axi->abs_timeout.tv_nsec); - break; } - case AUDIT_EXECVE: { struct audit_aux_data_execve *axi = (void *)aux; audit_log_execve_info(context, &ab, axi); @@ -2171,97 +2170,29 @@ int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr) } /** - * __audit_mq_timedsend - record audit data for a POSIX MQ timed send + * __audit_mq_sendrecv - record audit data for a POSIX MQ timed send/receive * @mqdes: MQ descriptor * @msg_len: Message length * @msg_prio: Message priority - * @u_abs_timeout: Message timeout in absolute time - * - * Returns 0 for success or NULL context or < 0 on error. - */ -int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, - const struct timespec __user *u_abs_timeout) -{ - struct audit_aux_data_mq_sendrecv *ax; - struct audit_context *context = current->audit_context; - - if (!audit_enabled) - return 0; - - if (likely(!context)) - return 0; - - ax = kmalloc(sizeof(*ax), GFP_ATOMIC); - if (!ax) - return -ENOMEM; - - if (u_abs_timeout != NULL) { - if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) { - kfree(ax); - return -EFAULT; - } - } else - memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout)); - - ax->mqdes = mqdes; - ax->msg_len = msg_len; - ax->msg_prio = msg_prio; - - ax->d.type = AUDIT_MQ_SENDRECV; - ax->d.next = context->aux; - context->aux = (void *)ax; - return 0; -} - -/** - * __audit_mq_timedreceive - record audit data for a POSIX MQ timed receive - * @mqdes: MQ descriptor - * @msg_len: Message length - * @u_msg_prio: Message priority - * @u_abs_timeout: Message timeout in absolute time + * @abs_timeout: Message timeout in absolute time * - * Returns 0 for success or NULL context or < 0 on error. */ -int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, - unsigned int __user *u_msg_prio, - const struct timespec __user *u_abs_timeout) +void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, + const struct timespec *abs_timeout) { - struct audit_aux_data_mq_sendrecv *ax; struct audit_context *context = current->audit_context; + struct timespec *p = &context->mq_sendrecv.abs_timeout; - if (!audit_enabled) - return 0; - - if (likely(!context)) - return 0; - - ax = kmalloc(sizeof(*ax), GFP_ATOMIC); - if (!ax) - return -ENOMEM; - - if (u_msg_prio != NULL) { - if (get_user(ax->msg_prio, u_msg_prio)) { - kfree(ax); - return -EFAULT; - } - } else - ax->msg_prio = 0; - - if (u_abs_timeout != NULL) { - if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) { - kfree(ax); - return -EFAULT; - } - } else - memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout)); + if (abs_timeout) + memcpy(p, abs_timeout, sizeof(struct timespec)); + else + memset(p, 0, sizeof(struct timespec)); - ax->mqdes = mqdes; - ax->msg_len = msg_len; + context->mq_sendrecv.mqdes = mqdes; + context->mq_sendrecv.msg_len = msg_len; + context->mq_sendrecv.msg_prio = msg_prio; - ax->d.type = AUDIT_MQ_SENDRECV; - ax->d.next = context->aux; - context->aux = (void *)ax; - return 0; + context->type = AUDIT_MQ_SENDRECV; } /** -- cgit v1.2.3 From 564f6993ffef656aebaf46cf2f1f6cb4f5c97207 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 14 Dec 2008 04:02:26 -0500 Subject: sanitize audit_mq_open() * don't bother with allocations * don't do double copy_from_user() * don't duplicate parts of check for audit_dummy_context() Signed-off-by: Al Viro --- include/linux/audit.h | 9 ++++--- ipc/mqueue.c | 23 +++++++++--------- kernel/auditsc.c | 65 ++++++++++++++++++--------------------------------- 3 files changed, 38 insertions(+), 59 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index 67f0cdd991ba..54978bdd2bd4 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -450,7 +450,7 @@ extern void audit_socketcall(int nargs, unsigned long *args); extern int audit_sockaddr(int len, void *addr); extern int __audit_fd_pair(int fd1, int fd2); extern int audit_set_macxattr(const char *name); -extern int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr); +extern void __audit_mq_open(int oflag, mode_t mode, struct mq_attr *attr); extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout); extern void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification); extern void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat); @@ -475,11 +475,10 @@ static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid if (unlikely(!audit_dummy_context())) __audit_ipc_set_perm(qbytes, uid, gid, mode); } -static inline int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr) +static inline void audit_mq_open(int oflag, mode_t mode, struct mq_attr *attr) { if (unlikely(!audit_dummy_context())) - return __audit_mq_open(oflag, mode, u_attr); - return 0; + __audit_mq_open(oflag, mode, attr); } static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout) { @@ -541,7 +540,7 @@ extern int audit_signals; #define audit_fd_pair(n,a) ({ 0; }) #define audit_sockaddr(len, addr) ({ 0; }) #define audit_set_macxattr(n) do { ; } while (0) -#define audit_mq_open(o,m,a) ({ 0; }) +#define audit_mq_open(o,m,a) ((void)0) #define audit_mq_sendrecv(d,l,p,t) ((void)0) #define audit_mq_notify(d,n) ((void)0) #define audit_mq_getsetattr(d,s) ((void)0) diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 192da806c283..d448b69672b5 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -588,22 +588,18 @@ static int mq_attr_ok(struct mq_attr *attr) * Invoked when creating a new queue via sys_mq_open */ static struct file *do_create(struct dentry *dir, struct dentry *dentry, - int oflag, mode_t mode, struct mq_attr __user *u_attr) + int oflag, mode_t mode, struct mq_attr *attr) { const struct cred *cred = current_cred(); - struct mq_attr attr; struct file *result; int ret; - if (u_attr) { - ret = -EFAULT; - if (copy_from_user(&attr, u_attr, sizeof(attr))) - goto out; + if (attr) { ret = -EINVAL; - if (!mq_attr_ok(&attr)) + if (!mq_attr_ok(attr)) goto out; /* store for use during create */ - dentry->d_fsdata = &attr; + dentry->d_fsdata = attr; } mode &= ~current->fs->umask; @@ -660,11 +656,13 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode, struct dentry *dentry; struct file *filp; char *name; + struct mq_attr attr; int fd, error; - error = audit_mq_open(oflag, mode, u_attr); - if (error != 0) - return error; + if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr))) + return -EFAULT; + + audit_mq_open(oflag, mode, u_attr ? &attr : NULL); if (IS_ERR(name = getname(u_name))) return PTR_ERR(name); @@ -690,7 +688,8 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode, filp = do_open(dentry, oflag); } else { filp = do_create(mqueue_mnt->mnt_root, dentry, - oflag, mode, u_attr); + oflag, mode, + u_attr ? &attr : NULL); } } else { error = -ENOENT; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 140c47453470..83e946f1cdde 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -124,13 +124,6 @@ struct audit_aux_data { /* Number of target pids per aux struct. */ #define AUDIT_AUX_PIDS 16 -struct audit_aux_data_mq_open { - struct audit_aux_data d; - int oflag; - mode_t mode; - struct mq_attr attr; -}; - struct audit_aux_data_execve { struct audit_aux_data d; int argc; @@ -242,6 +235,11 @@ struct audit_context { unsigned int msg_prio; struct timespec abs_timeout; } mq_sendrecv; + struct { + int oflag; + mode_t mode; + struct mq_attr attr; + } mq_open; }; #if AUDIT_DEBUG @@ -1263,6 +1261,16 @@ static void show_special(struct audit_context *context, int *call_panic) return; } break; } + case AUDIT_MQ_OPEN: { + audit_log_format(ab, + "oflag=0x%x mode=%#o mq_flags=0x%lx mq_maxmsg=%ld " + "mq_msgsize=%ld mq_curmsgs=%ld", + context->mq_open.oflag, context->mq_open.mode, + context->mq_open.attr.mq_flags, + context->mq_open.attr.mq_maxmsg, + context->mq_open.attr.mq_msgsize, + context->mq_open.attr.mq_curmsgs); + break; } case AUDIT_MQ_SENDRECV: { audit_log_format(ab, "mqdes=%d msg_len=%zd msg_prio=%u " @@ -1368,15 +1376,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts continue; /* audit_panic has been called */ switch (aux->type) { - case AUDIT_MQ_OPEN: { - struct audit_aux_data_mq_open *axi = (void *)aux; - audit_log_format(ab, - "oflag=0x%x mode=%#o mq_flags=0x%lx mq_maxmsg=%ld " - "mq_msgsize=%ld mq_curmsgs=%ld", - axi->oflag, axi->mode, axi->attr.mq_flags, - axi->attr.mq_maxmsg, axi->attr.mq_msgsize, - axi->attr.mq_curmsgs); - break; } case AUDIT_EXECVE: { struct audit_aux_data_execve *axi = (void *)aux; @@ -2135,38 +2134,20 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid) * @mode: mode bits * @u_attr: queue attributes * - * Returns 0 for success or NULL context or < 0 on error. */ -int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr) +void __audit_mq_open(int oflag, mode_t mode, struct mq_attr *attr) { - struct audit_aux_data_mq_open *ax; struct audit_context *context = current->audit_context; - if (!audit_enabled) - return 0; - - if (likely(!context)) - return 0; - - ax = kmalloc(sizeof(*ax), GFP_ATOMIC); - if (!ax) - return -ENOMEM; - - if (u_attr != NULL) { - if (copy_from_user(&ax->attr, u_attr, sizeof(ax->attr))) { - kfree(ax); - return -EFAULT; - } - } else - memset(&ax->attr, 0, sizeof(ax->attr)); + if (attr) + memcpy(&context->mq_open.attr, attr, sizeof(struct mq_attr)); + else + memset(&context->mq_open.attr, 0, sizeof(struct mq_attr)); - ax->oflag = oflag; - ax->mode = mode; + context->mq_open.oflag = oflag; + context->mq_open.mode = mode; - ax->d.type = AUDIT_MQ_OPEN; - ax->d.next = context->aux; - context->aux = (void *)ax; - return 0; + context->type = AUDIT_MQ_OPEN; } /** -- cgit v1.2.3 From 157cf649a735a2f7e8dba0ed08e6e38b6c30d886 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 14 Dec 2008 04:57:47 -0500 Subject: sanitize audit_fd_pair() * no allocations * return void Signed-off-by: Al Viro --- fs/pipe.c | 7 +------ include/linux/audit.h | 9 ++++----- kernel/auditsc.c | 44 ++++++++++++++------------------------------ net/socket.c | 9 +-------- 4 files changed, 20 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/fs/pipe.c b/fs/pipe.c index aaf797bd57b9..891697112f66 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1016,10 +1016,7 @@ int do_pipe_flags(int *fd, int flags) goto err_fdr; fdw = error; - error = audit_fd_pair(fdr, fdw); - if (error < 0) - goto err_fdw; - + audit_fd_pair(fdr, fdw); fd_install(fdr, fr); fd_install(fdw, fw); fd[0] = fdr; @@ -1027,8 +1024,6 @@ int do_pipe_flags(int *fd, int flags) return 0; - err_fdw: - put_unused_fd(fdw); err_fdr: put_unused_fd(fdr); err_read_pipe: diff --git a/include/linux/audit.h b/include/linux/audit.h index 54978bdd2bd4..bd59cd1e3219 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -448,7 +448,7 @@ extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mod extern int audit_bprm(struct linux_binprm *bprm); extern void audit_socketcall(int nargs, unsigned long *args); extern int audit_sockaddr(int len, void *addr); -extern int __audit_fd_pair(int fd1, int fd2); +extern void __audit_fd_pair(int fd1, int fd2); extern int audit_set_macxattr(const char *name); extern void __audit_mq_open(int oflag, mode_t mode, struct mq_attr *attr); extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout); @@ -464,11 +464,10 @@ static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp) if (unlikely(!audit_dummy_context())) __audit_ipc_obj(ipcp); } -static inline int audit_fd_pair(int fd1, int fd2) +static inline void audit_fd_pair(int fd1, int fd2) { if (unlikely(!audit_dummy_context())) - return __audit_fd_pair(fd1, fd2); - return 0; + __audit_fd_pair(fd1, fd2); } static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) { @@ -537,7 +536,7 @@ extern int audit_signals; #define audit_ipc_set_perm(q,u,g,m) ((void)0) #define audit_bprm(p) ({ 0; }) #define audit_socketcall(n,a) ((void)0) -#define audit_fd_pair(n,a) ({ 0; }) +#define audit_fd_pair(n,a) ((void)0) #define audit_sockaddr(len, addr) ({ 0; }) #define audit_set_macxattr(n) do { ; } while (0) #define audit_mq_open(o,m,a) ((void)0) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 83e946f1cdde..327e65d50674 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -131,11 +131,6 @@ struct audit_aux_data_execve { struct mm_struct *mm; }; -struct audit_aux_data_fd_pair { - struct audit_aux_data d; - int fd[2]; -}; - struct audit_aux_data_pids { struct audit_aux_data d; pid_t target_pid[AUDIT_AUX_PIDS]; @@ -241,6 +236,7 @@ struct audit_context { struct mq_attr attr; } mq_open; }; + int fds[2]; #if AUDIT_DEBUG int put_count; @@ -1382,11 +1378,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts audit_log_execve_info(context, &ab, axi); break; } - case AUDIT_FD_PAIR: { - struct audit_aux_data_fd_pair *axs = (void *)aux; - audit_log_format(ab, "fd0=%d fd1=%d", axs->fd[0], axs->fd[1]); - break; } - case AUDIT_BPRM_FCAPS: { struct audit_aux_data_bprm_fcaps *axs = (void *)aux; audit_log_format(ab, "fver=%x", axs->fcap_ver); @@ -1416,6 +1407,15 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts if (context->type) show_special(context, &call_panic); + if (context->fds[0] >= 0) { + ab = audit_log_start(context, GFP_KERNEL, AUDIT_FD_PAIR); + if (ab) { + audit_log_format(ab, "fd0=%d fd1=%d", + context->fds[0], context->fds[1]); + audit_log_end(ab); + } + } + if (context->sockaddr_len) { ab = audit_log_start(context, GFP_KERNEL, AUDIT_SOCKADDR); if (ab) { @@ -1696,6 +1696,7 @@ void audit_syscall_exit(int valid, long return_code) context->target_sid = 0; context->sockaddr_len = 0; context->type = 0; + context->fds[0] = -1; kfree(context->filterkey); context->filterkey = NULL; tsk->audit_context = context; @@ -2291,29 +2292,12 @@ void audit_socketcall(int nargs, unsigned long *args) * @fd1: the first file descriptor * @fd2: the second file descriptor * - * Returns 0 for success or NULL context or < 0 on error. */ -int __audit_fd_pair(int fd1, int fd2) +void __audit_fd_pair(int fd1, int fd2) { struct audit_context *context = current->audit_context; - struct audit_aux_data_fd_pair *ax; - - if (likely(!context)) { - return 0; - } - - ax = kmalloc(sizeof(*ax), GFP_KERNEL); - if (!ax) { - return -ENOMEM; - } - - ax->fd[0] = fd1; - ax->fd[1] = fd2; - - ax->d.type = AUDIT_FD_PAIR; - ax->d.next = context->aux; - context->aux = (void *)ax; - return 0; + context->fds[0] = fd1; + context->fds[1] = fd2; } /** diff --git a/net/socket.c b/net/socket.c index b41a92093e40..06603d73c411 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1313,13 +1313,7 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, goto out_fd1; } - err = audit_fd_pair(fd1, fd2); - if (err < 0) { - fput(newfile1); - fput(newfile2); - goto out_fd; - } - + audit_fd_pair(fd1, fd2); fd_install(fd1, newfile1); fd_install(fd2, newfile2); /* fd1 and fd2 may be already another descriptors. @@ -1349,7 +1343,6 @@ out_fd2: out_fd1: put_filp(newfile2); sock_release(sock2); -out_fd: put_unused_fd(fd1); put_unused_fd(fd2); goto out; -- cgit v1.2.3 From 57f71a0af4244d9ba3c0bce74b1d2e66e8d520bd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 4 Jan 2009 14:52:57 -0500 Subject: sanitize audit_log_capset() * no allocations * return void * don't duplicate checked for dummy context Signed-off-by: Al Viro --- include/linux/audit.h | 9 ++++----- kernel/auditsc.c | 44 ++++++++++++++++---------------------------- kernel/capability.c | 4 +--- 3 files changed, 21 insertions(+), 36 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index bd59cd1e3219..7ddcb6a29eb1 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -457,7 +457,7 @@ extern void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat); extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, const struct cred *new, const struct cred *old); -extern int __audit_log_capset(pid_t pid, const struct cred *new, const struct cred *old); +extern void __audit_log_capset(pid_t pid, const struct cred *new, const struct cred *old); static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp) { @@ -504,12 +504,11 @@ static inline int audit_log_bprm_fcaps(struct linux_binprm *bprm, return 0; } -static inline int audit_log_capset(pid_t pid, const struct cred *new, +static inline void audit_log_capset(pid_t pid, const struct cred *new, const struct cred *old) { if (unlikely(!audit_dummy_context())) - return __audit_log_capset(pid, new, old); - return 0; + __audit_log_capset(pid, new, old); } extern int audit_n_rules; @@ -544,7 +543,7 @@ extern int audit_signals; #define audit_mq_notify(d,n) ((void)0) #define audit_mq_getsetattr(d,s) ((void)0) #define audit_log_bprm_fcaps(b, ncr, ocr) ({ 0; }) -#define audit_log_capset(pid, ncr, ocr) ({ 0; }) +#define audit_log_capset(pid, ncr, ocr) ((void)0) #define audit_ptrace(t) ((void)0) #define audit_n_rules 0 #define audit_signals 0 diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 327e65d50674..c76a58215f54 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -235,6 +235,10 @@ struct audit_context { mode_t mode; struct mq_attr attr; } mq_open; + struct { + pid_t pid; + struct audit_cap_data cap; + } capset; }; int fds[2]; @@ -1291,6 +1295,12 @@ static void show_special(struct audit_context *context, int *call_panic) attr->mq_flags, attr->mq_maxmsg, attr->mq_msgsize, attr->mq_curmsgs); break; } + case AUDIT_CAPSET: { + audit_log_format(ab, "pid=%d", context->capset.pid); + audit_log_cap(ab, "cap_pi", &context->capset.cap.inheritable); + audit_log_cap(ab, "cap_pp", &context->capset.cap.permitted); + audit_log_cap(ab, "cap_pe", &context->capset.cap.effective); + break; } } audit_log_end(ab); } @@ -1392,14 +1402,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts audit_log_cap(ab, "new_pe", &axs->new_pcap.effective); break; } - case AUDIT_CAPSET: { - struct audit_aux_data_capset *axs = (void *)aux; - audit_log_format(ab, "pid=%d", axs->pid); - audit_log_cap(ab, "cap_pi", &axs->cap.inheritable); - audit_log_cap(ab, "cap_pp", &axs->cap.permitted); - audit_log_cap(ab, "cap_pe", &axs->cap.effective); - break; } - } audit_log_end(ab); } @@ -2456,29 +2458,15 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm, * Record the aguments userspace sent to sys_capset for later printing by the * audit system if applicable */ -int __audit_log_capset(pid_t pid, +void __audit_log_capset(pid_t pid, const struct cred *new, const struct cred *old) { - struct audit_aux_data_capset *ax; struct audit_context *context = current->audit_context; - - if (likely(!audit_enabled || !context || context->dummy)) - return 0; - - ax = kmalloc(sizeof(*ax), GFP_KERNEL); - if (!ax) - return -ENOMEM; - - ax->d.type = AUDIT_CAPSET; - ax->d.next = context->aux; - context->aux = (void *)ax; - - ax->pid = pid; - ax->cap.effective = new->cap_effective; - ax->cap.inheritable = new->cap_effective; - ax->cap.permitted = new->cap_permitted; - - return 0; + context->capset.pid = pid; + context->capset.cap.effective = new->cap_effective; + context->capset.cap.inheritable = new->cap_effective; + context->capset.cap.permitted = new->cap_permitted; + context->type = AUDIT_CAPSET; } /** diff --git a/kernel/capability.c b/kernel/capability.c index 36b4b4daebec..c598d9d5be4f 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -280,9 +280,7 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) if (ret < 0) goto error; - ret = audit_log_capset(pid, new, current_cred()); - if (ret < 0) - return ret; + audit_log_capset(pid, new, current_cred()); return commit_creds(new); -- cgit v1.2.3 From 0590b9335a1c72a3f0defcc6231287f7817e07c8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 14 Dec 2008 23:45:27 -0500 Subject: fixing audit rule ordering mess, part 1 Problem: ordering between the rules on exit chain is currently lost; all watch and inode rules are listed after everything else _and_ exit,never on one kind doesn't stop exit,always on another from being matched. Solution: assign priorities to rules, keep track of the current highest-priority matching rule and its result (always/never). Signed-off-by: Al Viro --- include/linux/audit.h | 1 + kernel/audit.h | 5 +--- kernel/auditfilter.c | 17 +++++++++-- kernel/auditsc.c | 79 ++++++++++++++++++++++++++++----------------------- 4 files changed, 59 insertions(+), 43 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index 7ddcb6a29eb1..5b47eeb00d53 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -373,6 +373,7 @@ struct audit_krule { struct audit_watch *watch; /* associated watch */ struct audit_tree *tree; /* associated watched tree */ struct list_head rlist; /* entry in audit_{watch,tree}.rules list */ + u64 prio; }; struct audit_field { diff --git a/kernel/audit.h b/kernel/audit.h index 9d6717412fec..16f18cac661b 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -159,11 +159,8 @@ static inline int audit_signal_info(int sig, struct task_struct *t) return __audit_signal_info(sig, t); return 0; } -extern enum audit_state audit_filter_inodes(struct task_struct *, - struct audit_context *); -extern void audit_set_auditable(struct audit_context *); +extern void audit_filter_inodes(struct task_struct *, struct audit_context *); #else #define audit_signal_info(s,t) AUDIT_DISABLED #define audit_filter_inodes(t,c) AUDIT_DISABLED -#define audit_set_auditable(c) #endif diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 0febaa0f784c..995a2e86808d 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -919,6 +919,7 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old, new->action = old->action; for (i = 0; i < AUDIT_BITMASK_SIZE; i++) new->mask[i] = old->mask[i]; + new->prio = old->prio; new->buflen = old->buflen; new->inode_f = old->inode_f; new->watch = NULL; @@ -987,9 +988,8 @@ static void audit_update_watch(struct audit_parent *parent, /* If the update involves invalidating rules, do the inode-based * filtering now, so we don't omit records. */ - if (invalidating && current->audit_context && - audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT) - audit_set_auditable(current->audit_context); + if (invalidating && current->audit_context) + audit_filter_inodes(current, current->audit_context); nwatch = audit_dupe_watch(owatch); if (IS_ERR(nwatch)) { @@ -1258,6 +1258,9 @@ static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp, return ret; } +static u64 prio_low = ~0ULL/2; +static u64 prio_high = ~0ULL/2 - 1; + /* Add rule to given filterlist if not a duplicate. */ static inline int audit_add_rule(struct audit_entry *entry, struct list_head *list) @@ -1319,6 +1322,14 @@ static inline int audit_add_rule(struct audit_entry *entry, } } + entry->rule.prio = ~0ULL; + if (entry->rule.listnr == AUDIT_FILTER_EXIT) { + if (entry->rule.flags & AUDIT_FILTER_PREPEND) + entry->rule.prio = ++prio_high; + else + entry->rule.prio = --prio_low; + } + if (entry->rule.flags & AUDIT_FILTER_PREPEND) { list_add_rcu(&entry->list, list); entry->rule.flags &= ~AUDIT_FILTER_PREPEND; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index c76a58215f54..19d2c2747c8d 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -165,14 +165,14 @@ struct audit_tree_refs { struct audit_context { int dummy; /* must be the first element */ int in_syscall; /* 1 if task is in a syscall */ - enum audit_state state; + enum audit_state state, current_state; unsigned int serial; /* serial number for record */ struct timespec ctime; /* time of syscall entry */ int major; /* syscall number */ unsigned long argv[4]; /* syscall arguments */ int return_valid; /* return code is valid */ long return_code;/* syscall return code */ - int auditable; /* 1 if record should be written */ + u64 prio; int name_count; struct audit_names names[AUDIT_NAMES]; char * filterkey; /* key for rule that triggered record */ @@ -630,8 +630,16 @@ static int audit_filter_rules(struct task_struct *tsk, return 0; } } - if (rule->filterkey && ctx) - ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC); + + if (ctx) { + if (rule->prio <= ctx->prio) + return 0; + if (rule->filterkey) { + kfree(ctx->filterkey); + ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC); + } + ctx->prio = rule->prio; + } switch (rule->action) { case AUDIT_NEVER: *state = AUDIT_DISABLED; break; case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; @@ -685,6 +693,7 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, audit_filter_rules(tsk, &e->rule, ctx, NULL, &state)) { rcu_read_unlock(); + ctx->current_state = state; return state; } } @@ -698,15 +707,14 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, * buckets applicable to the inode numbers in audit_names[]. * Regarding audit_state, same rules apply as for audit_filter_syscall(). */ -enum audit_state audit_filter_inodes(struct task_struct *tsk, - struct audit_context *ctx) +void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx) { int i; struct audit_entry *e; enum audit_state state; if (audit_pid && tsk->tgid == audit_pid) - return AUDIT_DISABLED; + return; rcu_read_lock(); for (i = 0; i < ctx->name_count; i++) { @@ -723,17 +731,20 @@ enum audit_state audit_filter_inodes(struct task_struct *tsk, if ((e->rule.mask[word] & bit) == bit && audit_filter_rules(tsk, &e->rule, ctx, n, &state)) { rcu_read_unlock(); - return state; + ctx->current_state = state; + return; } } } rcu_read_unlock(); - return AUDIT_BUILD_CONTEXT; } -void audit_set_auditable(struct audit_context *ctx) +static void audit_set_auditable(struct audit_context *ctx) { - ctx->auditable = 1; + if (!ctx->prio) { + ctx->prio = 1; + ctx->current_state = AUDIT_RECORD_CONTEXT; + } } static inline struct audit_context *audit_get_context(struct task_struct *tsk, @@ -764,23 +775,11 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk, else context->return_code = return_code; - if (context->in_syscall && !context->dummy && !context->auditable) { - enum audit_state state; - - state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]); - if (state == AUDIT_RECORD_CONTEXT) { - context->auditable = 1; - goto get_context; - } - - state = audit_filter_inodes(tsk, context); - if (state == AUDIT_RECORD_CONTEXT) - context->auditable = 1; - + if (context->in_syscall && !context->dummy) { + audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]); + audit_filter_inodes(tsk, context); } -get_context: - tsk->audit_context = NULL; return context; } @@ -790,8 +789,7 @@ static inline void audit_free_names(struct audit_context *context) int i; #if AUDIT_DEBUG == 2 - if (context->auditable - ||context->put_count + context->ino_count != context->name_count) { + if (context->put_count + context->ino_count != context->name_count) { printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d" " name_count=%d put_count=%d" " ino_count=%d [NOT freeing]\n", @@ -842,6 +840,7 @@ static inline void audit_zero_context(struct audit_context *context, { memset(context, 0, sizeof(*context)); context->state = state; + context->prio = state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0; } static inline struct audit_context *audit_alloc_context(enum audit_state state) @@ -1543,7 +1542,7 @@ void audit_free(struct task_struct *tsk) * We use GFP_ATOMIC here because we might be doing this * in the context of the idle thread */ /* that can happen only if we are called from do_exit() */ - if (context->in_syscall && context->auditable) + if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT) audit_log_exit(context, tsk); audit_free_context(context); @@ -1627,15 +1626,17 @@ void audit_syscall_entry(int arch, int major, state = context->state; context->dummy = !audit_n_rules; - if (!context->dummy && (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT)) + if (!context->dummy && state == AUDIT_BUILD_CONTEXT) { + context->prio = 0; state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]); + } if (likely(state == AUDIT_DISABLED)) return; context->serial = 0; context->ctime = CURRENT_TIME; context->in_syscall = 1; - context->auditable = !!(state == AUDIT_RECORD_CONTEXT); + context->current_state = state; context->ppid = 0; } @@ -1643,17 +1644,20 @@ void audit_finish_fork(struct task_struct *child) { struct audit_context *ctx = current->audit_context; struct audit_context *p = child->audit_context; - if (!p || !ctx || !ctx->auditable) + if (!p || !ctx) + return; + if (!ctx->in_syscall || ctx->current_state != AUDIT_RECORD_CONTEXT) return; p->arch = ctx->arch; p->major = ctx->major; memcpy(p->argv, ctx->argv, sizeof(ctx->argv)); p->ctime = ctx->ctime; p->dummy = ctx->dummy; - p->auditable = ctx->auditable; p->in_syscall = ctx->in_syscall; p->filterkey = kstrdup(ctx->filterkey, GFP_KERNEL); p->ppid = current->pid; + p->prio = ctx->prio; + p->current_state = ctx->current_state; } /** @@ -1677,11 +1681,11 @@ void audit_syscall_exit(int valid, long return_code) if (likely(!context)) return; - if (context->in_syscall && context->auditable) + if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT) audit_log_exit(context, tsk); context->in_syscall = 0; - context->auditable = 0; + context->prio = context->state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0; if (context->previous) { struct audit_context *new_context = context->previous; @@ -2091,7 +2095,10 @@ int auditsc_get_stamp(struct audit_context *ctx, t->tv_sec = ctx->ctime.tv_sec; t->tv_nsec = ctx->ctime.tv_nsec; *serial = ctx->serial; - ctx->auditable = 1; + if (!ctx->prio) { + ctx->prio = 1; + ctx->current_state = AUDIT_RECORD_CONTEXT; + } return 1; } -- cgit v1.2.3 From e45aa212ea81d39b38ba158df344dc3a500153e5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 15 Dec 2008 01:17:50 -0500 Subject: audit rules ordering, part 2 Fix the actual rule listing; add per-type lists _not_ used for matching, with all exit,... sitting on one such list. Simplifies "do something for all rules" logics, while we are at it... Signed-off-by: Al Viro --- include/linux/audit.h | 1 + kernel/audit_tree.c | 1 + kernel/auditfilter.c | 95 +++++++++++++++++++++------------------------------ 3 files changed, 41 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index 5b47eeb00d53..cc71fdb56ae2 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -373,6 +373,7 @@ struct audit_krule { struct audit_watch *watch; /* associated watch */ struct audit_tree *tree; /* associated watched tree */ struct list_head rlist; /* entry in audit_{watch,tree}.rules list */ + struct list_head list; /* for AUDIT_LIST* purposes only */ u64 prio; }; diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 8b509441f49a..48bddad2a3dc 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -450,6 +450,7 @@ static void kill_rules(struct audit_tree *tree) audit_log_end(ab); rule->tree = NULL; list_del_rcu(&entry->list); + list_del(&entry->rule.list); call_rcu(&entry->rcu, audit_free_rule_rcu); } } diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 995a2e86808d..5d4edc6f7a32 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -86,6 +86,14 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { #error Fix audit_filter_list initialiser #endif }; +static struct list_head audit_rules_list[AUDIT_NR_FILTERS] = { + LIST_HEAD_INIT(audit_rules_list[0]), + LIST_HEAD_INIT(audit_rules_list[1]), + LIST_HEAD_INIT(audit_rules_list[2]), + LIST_HEAD_INIT(audit_rules_list[3]), + LIST_HEAD_INIT(audit_rules_list[4]), + LIST_HEAD_INIT(audit_rules_list[5]), +}; DEFINE_MUTEX(audit_filter_mutex); @@ -1007,12 +1015,15 @@ static void audit_update_watch(struct audit_parent *parent, list_del_rcu(&oentry->list); nentry = audit_dupe_rule(&oentry->rule, nwatch); - if (IS_ERR(nentry)) + if (IS_ERR(nentry)) { + list_del(&oentry->rule.list); audit_panic("error updating watch, removing"); - else { + } else { int h = audit_hash_ino((u32)ino); list_add(&nentry->rule.rlist, &nwatch->rules); list_add_rcu(&nentry->list, &audit_inode_hash[h]); + list_replace(&oentry->rule.list, + &nentry->rule.list); } call_rcu(&oentry->rcu, audit_free_rule_rcu); @@ -1077,6 +1088,7 @@ static void audit_remove_parent_watches(struct audit_parent *parent) audit_log_end(ab); } list_del(&r->rlist); + list_del(&r->list); list_del_rcu(&e->list); call_rcu(&e->rcu, audit_free_rule_rcu); } @@ -1331,9 +1343,13 @@ static inline int audit_add_rule(struct audit_entry *entry, } if (entry->rule.flags & AUDIT_FILTER_PREPEND) { + list_add(&entry->rule.list, + &audit_rules_list[entry->rule.listnr]); list_add_rcu(&entry->list, list); entry->rule.flags &= ~AUDIT_FILTER_PREPEND; } else { + list_add_tail(&entry->rule.list, + &audit_rules_list[entry->rule.listnr]); list_add_tail_rcu(&entry->list, list); } #ifdef CONFIG_AUDITSYSCALL @@ -1415,6 +1431,7 @@ static inline int audit_del_rule(struct audit_entry *entry, audit_remove_tree_rule(&e->rule); list_del_rcu(&e->list); + list_del(&e->rule.list); call_rcu(&e->rcu, audit_free_rule_rcu); #ifdef CONFIG_AUDITSYSCALL @@ -1443,30 +1460,16 @@ out: static void audit_list(int pid, int seq, struct sk_buff_head *q) { struct sk_buff *skb; - struct audit_entry *entry; + struct audit_krule *r; int i; /* This is a blocking read, so use audit_filter_mutex instead of rcu * iterator to sync with list writers. */ for (i=0; irule); - if (unlikely(!rule)) - break; - skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1, - rule, sizeof(*rule)); - if (skb) - skb_queue_tail(q, skb); - kfree(rule); - } - } - for (i = 0; i < AUDIT_INODE_BUCKETS; i++) { - list_for_each_entry(entry, &audit_inode_hash[i], list) { + list_for_each_entry(r, &audit_rules_list[i], list) { struct audit_rule *rule; - rule = audit_krule_to_rule(&entry->rule); + rule = audit_krule_to_rule(r); if (unlikely(!rule)) break; skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1, @@ -1485,30 +1488,16 @@ static void audit_list(int pid, int seq, struct sk_buff_head *q) static void audit_list_rules(int pid, int seq, struct sk_buff_head *q) { struct sk_buff *skb; - struct audit_entry *e; + struct audit_krule *r; int i; /* This is a blocking read, so use audit_filter_mutex instead of rcu * iterator to sync with list writers. */ for (i=0; irule); - if (unlikely(!data)) - break; - skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, - data, sizeof(*data) + data->buflen); - if (skb) - skb_queue_tail(q, skb); - kfree(data); - } - } - for (i=0; i< AUDIT_INODE_BUCKETS; i++) { - list_for_each_entry(e, &audit_inode_hash[i], list) { + list_for_each_entry(r, &audit_rules_list[i], list) { struct audit_rule_data *data; - data = audit_krule_to_data(&e->rule); + data = audit_krule_to_data(r); if (unlikely(!data)) break; skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, @@ -1789,35 +1778,37 @@ unlock_and_return: return result; } -static int update_lsm_rule(struct audit_entry *entry) +static int update_lsm_rule(struct audit_krule *r) { + struct audit_entry *entry = container_of(r, struct audit_entry, rule); struct audit_entry *nentry; struct audit_watch *watch; struct audit_tree *tree; int err = 0; - if (!security_audit_rule_known(&entry->rule)) + if (!security_audit_rule_known(r)) return 0; - watch = entry->rule.watch; - tree = entry->rule.tree; - nentry = audit_dupe_rule(&entry->rule, watch); + watch = r->watch; + tree = r->tree; + nentry = audit_dupe_rule(r, watch); if (IS_ERR(nentry)) { /* save the first error encountered for the * return value */ err = PTR_ERR(nentry); audit_panic("error updating LSM filters"); if (watch) - list_del(&entry->rule.rlist); + list_del(&r->rlist); list_del_rcu(&entry->list); + list_del(&r->list); } else { if (watch) { list_add(&nentry->rule.rlist, &watch->rules); - list_del(&entry->rule.rlist); + list_del(&r->rlist); } else if (tree) - list_replace_init(&entry->rule.rlist, - &nentry->rule.rlist); + list_replace_init(&r->rlist, &nentry->rule.rlist); list_replace_rcu(&entry->list, &nentry->list); + list_replace(&r->list, &nentry->rule.list); } call_rcu(&entry->rcu, audit_free_rule_rcu); @@ -1831,27 +1822,19 @@ static int update_lsm_rule(struct audit_entry *entry) * updated rule. */ int audit_update_lsm_rules(void) { - struct audit_entry *e, *n; + struct audit_krule *r, *n; int i, err = 0; /* audit_filter_mutex synchronizes the writers */ mutex_lock(&audit_filter_mutex); for (i = 0; i < AUDIT_NR_FILTERS; i++) { - list_for_each_entry_safe(e, n, &audit_filter_list[i], list) { - int res = update_lsm_rule(e); - if (!err) - err = res; - } - } - for (i=0; i< AUDIT_INODE_BUCKETS; i++) { - list_for_each_entry_safe(e, n, &audit_inode_hash[i], list) { - int res = update_lsm_rule(e); + list_for_each_entry_safe(r, n, &audit_rules_list[i], list) { + int res = update_lsm_rule(r); if (!err) err = res; } } - mutex_unlock(&audit_filter_mutex); return err; -- cgit v1.2.3 From 5af75d8d58d0f9f7b7c0515b35786b22892d5f12 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Dec 2008 05:59:26 -0500 Subject: audit: validate comparison operations, store them in sane form Don't store the field->op in the messy (and very inconvenient for e.g. audit_comparator()) form; translate to dense set of values and do full validation of userland-submitted value while we are at it. ->audit_init_rule() and ->audit_match_rule() get new values now; in-tree instances updated. Signed-off-by: Al Viro --- include/linux/audit.h | 12 ++++ kernel/audit_tree.c | 2 +- kernel/auditfilter.c | 132 ++++++++++++++++++++--------------------- security/selinux/ss/services.c | 26 ++++---- security/smack/smack_lsm.c | 6 +- 5 files changed, 94 insertions(+), 84 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index cc71fdb56ae2..67e5dbfc2961 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -247,6 +247,18 @@ #define AUDIT_GREATER_THAN_OR_EQUAL (AUDIT_GREATER_THAN|AUDIT_EQUAL) #define AUDIT_OPERATORS (AUDIT_EQUAL|AUDIT_NOT_EQUAL|AUDIT_BIT_MASK) +enum { + Audit_equal, + Audit_not_equal, + Audit_bitmask, + Audit_bittest, + Audit_lt, + Audit_gt, + Audit_le, + Audit_ge, + Audit_bad +}; + /* Status symbols */ /* Mask values */ #define AUDIT_STATUS_ENABLED 0x0001 diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 48bddad2a3dc..8ad9545b8db9 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -618,7 +618,7 @@ int audit_make_tree(struct audit_krule *rule, char *pathname, u32 op) if (pathname[0] != '/' || rule->listnr != AUDIT_FILTER_EXIT || - op & ~AUDIT_EQUAL || + op != Audit_equal || rule->inode_f || rule->watch || rule->tree) return -EINVAL; rule->tree = alloc_tree(pathname); diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index e6e3829cadd1..fbf24d121d97 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -252,7 +252,8 @@ static inline int audit_to_inode(struct audit_krule *krule, struct audit_field *f) { if (krule->listnr != AUDIT_FILTER_EXIT || - krule->watch || krule->inode_f || krule->tree) + krule->watch || krule->inode_f || krule->tree || + (f->op != Audit_equal && f->op != Audit_not_equal)) return -EINVAL; krule->inode_f = f; @@ -270,7 +271,7 @@ static int audit_to_watch(struct audit_krule *krule, char *path, int len, if (path[0] != '/' || path[len-1] == '/' || krule->listnr != AUDIT_FILTER_EXIT || - op & ~AUDIT_EQUAL || + op != Audit_equal || krule->inode_f || krule->watch || krule->tree) return -EINVAL; @@ -420,12 +421,32 @@ exit_err: return ERR_PTR(err); } +static u32 audit_ops[] = +{ + [Audit_equal] = AUDIT_EQUAL, + [Audit_not_equal] = AUDIT_NOT_EQUAL, + [Audit_bitmask] = AUDIT_BIT_MASK, + [Audit_bittest] = AUDIT_BIT_TEST, + [Audit_lt] = AUDIT_LESS_THAN, + [Audit_gt] = AUDIT_GREATER_THAN, + [Audit_le] = AUDIT_LESS_THAN_OR_EQUAL, + [Audit_ge] = AUDIT_GREATER_THAN_OR_EQUAL, +}; + +static u32 audit_to_op(u32 op) +{ + u32 n; + for (n = Audit_equal; n < Audit_bad && audit_ops[n] != op; n++) + ; + return n; +} + + /* Translate struct audit_rule to kernel's rule respresentation. * Exists for backward compatibility with userspace. */ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) { struct audit_entry *entry; - struct audit_field *ino_f; int err = 0; int i; @@ -435,12 +456,28 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) for (i = 0; i < rule->field_count; i++) { struct audit_field *f = &entry->rule.fields[i]; + u32 n; + + n = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); + + /* Support for legacy operators where + * AUDIT_NEGATE bit signifies != and otherwise assumes == */ + if (n & AUDIT_NEGATE) + f->op = Audit_not_equal; + else if (!n) + f->op = Audit_equal; + else + f->op = audit_to_op(n); + + entry->rule.vers_ops = (n & AUDIT_OPERATORS) ? 2 : 1; - f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); f->val = rule->values[i]; err = -EINVAL; + if (f->op == Audit_bad) + goto exit_free; + switch(f->type) { default: goto exit_free; @@ -462,11 +499,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) case AUDIT_EXIT: case AUDIT_SUCCESS: /* bit ops are only useful on syscall args */ - if (f->op == AUDIT_BIT_MASK || - f->op == AUDIT_BIT_TEST) { - err = -EINVAL; + if (f->op == Audit_bitmask || f->op == Audit_bittest) goto exit_free; - } break; case AUDIT_ARG0: case AUDIT_ARG1: @@ -475,11 +509,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) break; /* arch is only allowed to be = or != */ case AUDIT_ARCH: - if ((f->op != AUDIT_NOT_EQUAL) && (f->op != AUDIT_EQUAL) - && (f->op != AUDIT_NEGATE) && (f->op)) { - err = -EINVAL; + if (f->op != Audit_not_equal && f->op != Audit_equal) goto exit_free; - } entry->rule.arch_f = f; break; case AUDIT_PERM: @@ -496,33 +527,10 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) goto exit_free; break; } - - entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; - - /* Support for legacy operators where - * AUDIT_NEGATE bit signifies != and otherwise assumes == */ - if (f->op & AUDIT_NEGATE) - f->op = AUDIT_NOT_EQUAL; - else if (!f->op) - f->op = AUDIT_EQUAL; - else if (f->op == AUDIT_OPERATORS) { - err = -EINVAL; - goto exit_free; - } } - ino_f = entry->rule.inode_f; - if (ino_f) { - switch(ino_f->op) { - case AUDIT_NOT_EQUAL: - entry->rule.inode_f = NULL; - case AUDIT_EQUAL: - break; - default: - err = -EINVAL; - goto exit_free; - } - } + if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal) + entry->rule.inode_f = NULL; exit_nofree: return entry; @@ -538,7 +546,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, { int err = 0; struct audit_entry *entry; - struct audit_field *ino_f; void *bufp; size_t remain = datasz - sizeof(struct audit_rule_data); int i; @@ -554,11 +561,11 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, struct audit_field *f = &entry->rule.fields[i]; err = -EINVAL; - if (!(data->fieldflags[i] & AUDIT_OPERATORS) || - data->fieldflags[i] & ~AUDIT_OPERATORS) + + f->op = audit_to_op(data->fieldflags[i]); + if (f->op == Audit_bad) goto exit_free; - f->op = data->fieldflags[i] & AUDIT_OPERATORS; f->type = data->fields[i]; f->val = data->values[i]; f->lsm_str = NULL; @@ -670,18 +677,8 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, } } - ino_f = entry->rule.inode_f; - if (ino_f) { - switch(ino_f->op) { - case AUDIT_NOT_EQUAL: - entry->rule.inode_f = NULL; - case AUDIT_EQUAL: - break; - default: - err = -EINVAL; - goto exit_free; - } - } + if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal) + entry->rule.inode_f = NULL; exit_nofree: return entry; @@ -721,10 +718,10 @@ static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule) rule->fields[i] = krule->fields[i].type; if (krule->vers_ops == 1) { - if (krule->fields[i].op & AUDIT_NOT_EQUAL) + if (krule->fields[i].op == Audit_not_equal) rule->fields[i] |= AUDIT_NEGATE; } else { - rule->fields[i] |= krule->fields[i].op; + rule->fields[i] |= audit_ops[krule->fields[i].op]; } } for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i]; @@ -752,7 +749,7 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) struct audit_field *f = &krule->fields[i]; data->fields[i] = f->type; - data->fieldflags[i] = f->op; + data->fieldflags[i] = audit_ops[f->op]; switch(f->type) { case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: @@ -1626,28 +1623,29 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, return err; } -int audit_comparator(const u32 left, const u32 op, const u32 right) +int audit_comparator(u32 left, u32 op, u32 right) { switch (op) { - case AUDIT_EQUAL: + case Audit_equal: return (left == right); - case AUDIT_NOT_EQUAL: + case Audit_not_equal: return (left != right); - case AUDIT_LESS_THAN: + case Audit_lt: return (left < right); - case AUDIT_LESS_THAN_OR_EQUAL: + case Audit_le: return (left <= right); - case AUDIT_GREATER_THAN: + case Audit_gt: return (left > right); - case AUDIT_GREATER_THAN_OR_EQUAL: + case Audit_ge: return (left >= right); - case AUDIT_BIT_MASK: + case Audit_bitmask: return (left & right); - case AUDIT_BIT_TEST: + case Audit_bittest: return ((left & right) == right); + default: + BUG(); + return 0; } - BUG(); - return 0; } /* Compare given dentry name with last component in given path, diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 343c8ab14af0..c65e4fe4a0f1 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2602,7 +2602,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) case AUDIT_OBJ_ROLE: case AUDIT_OBJ_TYPE: /* only 'equals' and 'not equals' fit user, role, and type */ - if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL) + if (op != Audit_equal && op != Audit_not_equal) return -EINVAL; break; case AUDIT_SUBJ_SEN: @@ -2736,10 +2736,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, case AUDIT_SUBJ_USER: case AUDIT_OBJ_USER: switch (op) { - case AUDIT_EQUAL: + case Audit_equal: match = (ctxt->user == rule->au_ctxt.user); break; - case AUDIT_NOT_EQUAL: + case Audit_not_equal: match = (ctxt->user != rule->au_ctxt.user); break; } @@ -2747,10 +2747,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, case AUDIT_SUBJ_ROLE: case AUDIT_OBJ_ROLE: switch (op) { - case AUDIT_EQUAL: + case Audit_equal: match = (ctxt->role == rule->au_ctxt.role); break; - case AUDIT_NOT_EQUAL: + case Audit_not_equal: match = (ctxt->role != rule->au_ctxt.role); break; } @@ -2758,10 +2758,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, case AUDIT_SUBJ_TYPE: case AUDIT_OBJ_TYPE: switch (op) { - case AUDIT_EQUAL: + case Audit_equal: match = (ctxt->type == rule->au_ctxt.type); break; - case AUDIT_NOT_EQUAL: + case Audit_not_equal: match = (ctxt->type != rule->au_ctxt.type); break; } @@ -2774,31 +2774,31 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, field == AUDIT_OBJ_LEV_LOW) ? &ctxt->range.level[0] : &ctxt->range.level[1]); switch (op) { - case AUDIT_EQUAL: + case Audit_equal: match = mls_level_eq(&rule->au_ctxt.range.level[0], level); break; - case AUDIT_NOT_EQUAL: + case Audit_not_equal: match = !mls_level_eq(&rule->au_ctxt.range.level[0], level); break; - case AUDIT_LESS_THAN: + case Audit_lt: match = (mls_level_dom(&rule->au_ctxt.range.level[0], level) && !mls_level_eq(&rule->au_ctxt.range.level[0], level)); break; - case AUDIT_LESS_THAN_OR_EQUAL: + case Audit_le: match = mls_level_dom(&rule->au_ctxt.range.level[0], level); break; - case AUDIT_GREATER_THAN: + case Audit_gt: match = (mls_level_dom(level, &rule->au_ctxt.range.level[0]) && !mls_level_eq(level, &rule->au_ctxt.range.level[0])); break; - case AUDIT_GREATER_THAN_OR_EQUAL: + case Audit_ge: match = mls_level_dom(level, &rule->au_ctxt.range.level[0]); break; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 1b5551dfc1f7..848212fd4845 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2492,7 +2492,7 @@ static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER) return -EINVAL; - if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL) + if (op != Audit_equal && op != Audit_not_equal) return -EINVAL; *rule = smk_import(rulestr, 0); @@ -2556,9 +2556,9 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule, * both pointers will point to the same smack_known * label. */ - if (op == AUDIT_EQUAL) + if (op == Audit_equal) return (rule == smack); - if (op == AUDIT_NOT_EQUAL) + if (op == Audit_not_equal) return (rule != smack); return 0; -- cgit v1.2.3 From 0a30c5cefa53cbac429dcb2de906c0637b646253 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 4 Jan 2009 12:00:47 -0800 Subject: spi.h uses/needs device.h Include header files as used/needed: In file included from drivers/leds/leds-dac124s085.c:16: include/linux/spi/spi.h:66: error: field 'dev' has incomplete type include/linux/spi/spi.h: In function 'to_spi_device': include/linux/spi/spi.h:100: warning: type defaults to 'int' in declaration of '__mptr' ... Signed-off-by: Randy Dunlap Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/spi/spi.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 4be01bb44377..82229317753d 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -19,6 +19,8 @@ #ifndef __LINUX_SPI_H #define __LINUX_SPI_H +#include + /* * INTERFACES between SPI master-side drivers and SPI infrastructure. * (There's no SPI slave support for Linux yet...) -- cgit v1.2.3 From c644f0e4b56f9a2fc066cd0d75a18074d130e4a3 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Sun, 4 Jan 2009 12:00:48 -0800 Subject: fs: introduce bgl_lock_ptr() As suggested by Andreas Dilger, introduce a bgl_lock_ptr() helper in and add separate sb_bgl_lock() helpers to filesystem specific header files to break the hidden dependency to struct ext[234]_sb_info. Also, while at it, convert the macros to static inlines to try make up for all the times I broke Andrew Morton's tree. Acked-by: Andreas Dilger Signed-off-by: Pekka Enberg Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext4/ext4_sb.h | 6 ++++++ include/linux/blockgroup_lock.h | 7 +++++-- include/linux/ext2_fs_sb.h | 6 ++++++ include/linux/ext3_fs_sb.h | 6 ++++++ 4 files changed, 23 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/ext4/ext4_sb.h b/fs/ext4/ext4_sb.h index 445fde603df8..b21f16713db0 100644 --- a/fs/ext4/ext4_sb.h +++ b/fs/ext4/ext4_sb.h @@ -146,4 +146,10 @@ struct ext4_sb_info { struct flex_groups *s_flex_groups; }; +static inline spinlock_t * +sb_bgl_lock(struct ext4_sb_info *sbi, unsigned int block_group) +{ + return bgl_lock_ptr(&sbi->s_blockgroup_lock, block_group); +} + #endif /* _EXT4_SB */ diff --git a/include/linux/blockgroup_lock.h b/include/linux/blockgroup_lock.h index 8607312983bd..e44b88ba552b 100644 --- a/include/linux/blockgroup_lock.h +++ b/include/linux/blockgroup_lock.h @@ -53,7 +53,10 @@ static inline void bgl_lock_init(struct blockgroup_lock *bgl) * The accessor is a macro so we can embed a blockgroup_lock into different * superblock types */ -#define sb_bgl_lock(sb, block_group) \ - (&(sb)->s_blockgroup_lock.locks[(block_group) & (NR_BG_LOCKS-1)].lock) +static inline spinlock_t * +bgl_lock_ptr(struct blockgroup_lock *bgl, unsigned int block_group) +{ + return &bgl->locks[(block_group) & (NR_BG_LOCKS-1)].lock; +} #endif diff --git a/include/linux/ext2_fs_sb.h b/include/linux/ext2_fs_sb.h index f273415ab6f1..dc541f3653d1 100644 --- a/include/linux/ext2_fs_sb.h +++ b/include/linux/ext2_fs_sb.h @@ -108,4 +108,10 @@ struct ext2_sb_info { struct ext2_reserve_window_node s_rsv_window_head; }; +static inline spinlock_t * +sb_bgl_lock(struct ext2_sb_info *sbi, unsigned int block_group) +{ + return bgl_lock_ptr(&sbi->s_blockgroup_lock, block_group); +} + #endif /* _LINUX_EXT2_FS_SB */ diff --git a/include/linux/ext3_fs_sb.h b/include/linux/ext3_fs_sb.h index b65f0288b842..e024e38248ff 100644 --- a/include/linux/ext3_fs_sb.h +++ b/include/linux/ext3_fs_sb.h @@ -83,4 +83,10 @@ struct ext3_sb_info { #endif }; +static inline spinlock_t * +sb_bgl_lock(struct ext3_sb_info *sbi, unsigned int block_group) +{ + return bgl_lock_ptr(&sbi->s_blockgroup_lock, block_group); +} + #endif /* _LINUX_EXT3_FS_SB */ -- cgit v1.2.3 From 54566b2c1594c2326a645a3551f9d989f7ba3c5e Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sun, 4 Jan 2009 12:00:53 -0800 Subject: fs: symlink write_begin allocation context fix With the write_begin/write_end aops, page_symlink was broken because it could no longer pass a GFP_NOFS type mask into the point where the allocations happened. They are done in write_begin, which would always assume that the filesystem can be entered from reclaim. This bug could cause filesystem deadlocks. The funny thing with having a gfp_t mask there is that it doesn't really allow the caller to arbitrarily tinker with the context in which it can be called. It couldn't ever be GFP_ATOMIC, for example, because it needs to take the page lock. The only thing any callers care about is __GFP_FS anyway, so turn that into a single flag. Add a new flag for write_begin, AOP_FLAG_NOFS. Filesystems can now act on this flag in their write_begin function. Change __grab_cache_page to accept a nofs argument as well, to honour that flag (while we're there, change the name to grab_cache_page_write_begin which is more instructive and does away with random leading underscores). This is really a more flexible way to go in the end anyway -- if a filesystem happens to want any extra allocations aside from the pagecache ones in ints write_begin function, it may now use GFP_KERNEL (rather than GFP_NOFS) for common case allocations (eg. ocfs2_alloc_write_ctxt, for a random example). [kosaki.motohiro@jp.fujitsu.com: fix ubifs] [kosaki.motohiro@jp.fujitsu.com: fix fuse] Signed-off-by: Nick Piggin Reviewed-by: KOSAKI Motohiro Cc: [2.6.28.x] Signed-off-by: KOSAKI Motohiro Signed-off-by: Andrew Morton [ Cleaned up the calling convention: just pass in the AOP flags untouched to the grab_cache_page_write_begin() function. That just simplifies everybody, and may even allow future expansion of the logic. - Linus ] Signed-off-by: Linus Torvalds --- fs/affs/file.c | 2 +- fs/afs/write.c | 2 +- fs/buffer.c | 4 ++-- fs/cifs/file.c | 2 +- fs/ecryptfs/mmap.c | 2 +- fs/ext3/inode.c | 2 +- fs/ext3/namei.c | 3 +-- fs/ext4/inode.c | 4 ++-- fs/ext4/namei.c | 3 +-- fs/fuse/file.c | 4 ++-- fs/gfs2/ops_address.c | 2 +- fs/hostfs/hostfs_kern.c | 2 +- fs/jffs2/file.c | 2 +- fs/libfs.c | 2 +- fs/namei.c | 13 +++++++++---- fs/nfs/file.c | 2 +- fs/reiserfs/inode.c | 2 +- fs/smbfs/file.c | 2 +- fs/ubifs/file.c | 9 +++++---- include/linux/fs.h | 5 ++++- include/linux/pagemap.h | 3 ++- mm/filemap.c | 13 +++++++++---- 22 files changed, 49 insertions(+), 36 deletions(-) (limited to 'include/linux') diff --git a/fs/affs/file.c b/fs/affs/file.c index 1377b1240b6e..9246cb4aa018 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -628,7 +628,7 @@ static int affs_write_begin_ofs(struct file *file, struct address_space *mapping } index = pos >> PAGE_CACHE_SHIFT; - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) return -ENOMEM; *pagep = page; diff --git a/fs/afs/write.c b/fs/afs/write.c index d6b85dab35fc..3fb36d433621 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -144,7 +144,7 @@ int afs_write_begin(struct file *file, struct address_space *mapping, candidate->state = AFS_WBACK_PENDING; init_waitqueue_head(&candidate->waitq); - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) { kfree(candidate); return -ENOMEM; diff --git a/fs/buffer.c b/fs/buffer.c index 776ae091d3b0..a13f09b696f7 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1996,7 +1996,7 @@ int block_write_begin(struct file *file, struct address_space *mapping, page = *pagep; if (page == NULL) { ownpage = 1; - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) { status = -ENOMEM; goto out; @@ -2502,7 +2502,7 @@ int nobh_write_begin(struct file *file, struct address_space *mapping, from = pos & (PAGE_CACHE_SIZE - 1); to = from + len; - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) return -ENOMEM; *pagep = page; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index b1e1fc6a6e6a..12bb656fbe75 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2074,7 +2074,7 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping, cFYI(1, ("write_begin from %lld len %d", (long long)pos, len)); - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) { rc = -ENOMEM; goto out; diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index 04d7b3fa1ac6..46cec2b69796 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c @@ -288,7 +288,7 @@ static int ecryptfs_write_begin(struct file *file, loff_t prev_page_end_size; int rc = 0; - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) return -ENOMEM; *pagep = page; diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index c4bdccf976b5..5fa453b49a64 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1161,7 +1161,7 @@ static int ext3_write_begin(struct file *file, struct address_space *mapping, to = from + len; retry: - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) return -ENOMEM; *pagep = page; diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 297ea8dfac7c..1dd2abe6313e 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -2175,8 +2175,7 @@ retry: * We have a transaction open. All is sweetness. It also sets * i_size in generic_commit_write(). */ - err = __page_symlink(inode, symname, l, - mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); + err = __page_symlink(inode, symname, l, 1); if (err) { drop_nlink(inode); unlock_new_inode(inode); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 7c3325e0b005..6702a49992a6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1346,7 +1346,7 @@ retry: goto out; } - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) { ext4_journal_stop(handle); ret = -ENOMEM; @@ -2550,7 +2550,7 @@ retry: goto out; } - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) { ext4_journal_stop(handle); ret = -ENOMEM; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index da98a9012fa5..9fd2a5e1be4d 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2212,8 +2212,7 @@ retry: * We have a transaction open. All is sweetness. It also sets * i_size in generic_commit_write(). */ - err = __page_symlink(inode, symname, l, - mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); + err = __page_symlink(inode, symname, l, 1); if (err) { clear_nlink(inode); unlock_new_inode(inode); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 34930a964b82..4c9ee7011265 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -646,7 +646,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, { pgoff_t index = pos >> PAGE_CACHE_SHIFT; - *pagep = __grab_cache_page(mapping, index); + *pagep = grab_cache_page_write_begin(mapping, index, flags); if (!*pagep) return -ENOMEM; return 0; @@ -779,7 +779,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req, break; err = -ENOMEM; - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, 0); if (!page) break; diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c index 27563816e1c5..15f710f2d4da 100644 --- a/fs/gfs2/ops_address.c +++ b/fs/gfs2/ops_address.c @@ -675,7 +675,7 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping, goto out_trans_fail; error = -ENOMEM; - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); *pagep = page; if (unlikely(!page)) goto out_endtrans; diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 3a31451ac170..5c538e0ec14b 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -501,7 +501,7 @@ int hostfs_write_begin(struct file *file, struct address_space *mapping, { pgoff_t index = pos >> PAGE_CACHE_SHIFT; - *pagep = __grab_cache_page(mapping, index); + *pagep = grab_cache_page_write_begin(mapping, index, flags); if (!*pagep) return -ENOMEM; return 0; diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 5a98aa87c853..5edc2bf20581 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -132,7 +132,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, uint32_t pageofs = index << PAGE_CACHE_SHIFT; int ret = 0; - pg = __grab_cache_page(mapping, index); + pg = grab_cache_page_write_begin(mapping, index, flags); if (!pg) return -ENOMEM; *pagep = pg; diff --git a/fs/libfs.c b/fs/libfs.c index e960a8321902..bdaec17fa388 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -360,7 +360,7 @@ int simple_write_begin(struct file *file, struct address_space *mapping, index = pos >> PAGE_CACHE_SHIFT; from = pos & (PAGE_CACHE_SIZE - 1); - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) return -ENOMEM; diff --git a/fs/namei.c b/fs/namei.c index dd5c9f0bf829..df2d3df4f049 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2817,18 +2817,23 @@ void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) } } -int __page_symlink(struct inode *inode, const char *symname, int len, - gfp_t gfp_mask) +/* + * The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS + */ +int __page_symlink(struct inode *inode, const char *symname, int len, int nofs) { struct address_space *mapping = inode->i_mapping; struct page *page; void *fsdata; int err; char *kaddr; + unsigned int flags = AOP_FLAG_UNINTERRUPTIBLE; + if (nofs) + flags |= AOP_FLAG_NOFS; retry: err = pagecache_write_begin(NULL, mapping, 0, len-1, - AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); + flags, &page, &fsdata); if (err) goto fail; @@ -2852,7 +2857,7 @@ fail: int page_symlink(struct inode *inode, const char *symname, int len) { return __page_symlink(inode, symname, len, - mapping_gfp_mask(inode->i_mapping)); + !(mapping_gfp_mask(inode->i_mapping) & __GFP_FS)); } const struct inode_operations page_symlink_inode_operations = { diff --git a/fs/nfs/file.c b/fs/nfs/file.c index d319b49f8f06..90f292b520d2 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -354,7 +354,7 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping, file->f_path.dentry->d_name.name, mapping->host->i_ino, len, (long long) pos); - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) return -ENOMEM; *pagep = page; diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 145c2d3e5e01..ed04f47007f8 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -2561,7 +2561,7 @@ static int reiserfs_write_begin(struct file *file, } index = pos >> PAGE_CACHE_SHIFT; - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) return -ENOMEM; *pagep = page; diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index e4f8d51a5553..92d5e8ffb639 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -297,7 +297,7 @@ static int smb_write_begin(struct file *file, struct address_space *mapping, struct page **pagep, void **fsdata) { pgoff_t index = pos >> PAGE_CACHE_SHIFT; - *pagep = __grab_cache_page(mapping, index); + *pagep = grab_cache_page_write_begin(mapping, index, flags); if (!*pagep) return -ENOMEM; return 0; diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index fe82d2464d46..bf37374567fa 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -219,7 +219,8 @@ static void release_existing_page_budget(struct ubifs_info *c) } static int write_begin_slow(struct address_space *mapping, - loff_t pos, unsigned len, struct page **pagep) + loff_t pos, unsigned len, struct page **pagep, + unsigned flags) { struct inode *inode = mapping->host; struct ubifs_info *c = inode->i_sb->s_fs_info; @@ -247,7 +248,7 @@ static int write_begin_slow(struct address_space *mapping, if (unlikely(err)) return err; - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (unlikely(!page)) { ubifs_release_budget(c, &req); return -ENOMEM; @@ -438,7 +439,7 @@ static int ubifs_write_begin(struct file *file, struct address_space *mapping, return -EROFS; /* Try out the fast-path part first */ - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (unlikely(!page)) return -ENOMEM; @@ -483,7 +484,7 @@ static int ubifs_write_begin(struct file *file, struct address_space *mapping, unlock_page(page); page_cache_release(page); - return write_begin_slow(mapping, pos, len, pagep); + return write_begin_slow(mapping, pos, len, pagep, flags); } /* diff --git a/include/linux/fs.h b/include/linux/fs.h index e2170ee21e18..f2a3010140e3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -423,6 +423,9 @@ enum positive_aop_returns { #define AOP_FLAG_UNINTERRUPTIBLE 0x0001 /* will not do a short write */ #define AOP_FLAG_CONT_EXPAND 0x0002 /* called from cont_expand */ +#define AOP_FLAG_NOFS 0x0004 /* used by filesystem to direct + * helper code (eg buffer layer) + * to clear GFP_FS from alloc */ /* * oh the beauties of C type declarations. @@ -2035,7 +2038,7 @@ extern int page_readlink(struct dentry *, char __user *, int); extern void *page_follow_link_light(struct dentry *, struct nameidata *); extern void page_put_link(struct dentry *, struct nameidata *, void *); extern int __page_symlink(struct inode *inode, const char *symname, int len, - gfp_t gfp_mask); + int nofs); extern int page_symlink(struct inode *inode, const char *symname, int len); extern const struct inode_operations page_symlink_inode_operations; extern int generic_readlink(struct dentry *, char __user *, int); diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 709742be02f0..01ca0856caff 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -241,7 +241,8 @@ unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t start, unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index, int tag, unsigned int nr_pages, struct page **pages); -struct page *__grab_cache_page(struct address_space *mapping, pgoff_t index); +struct page *grab_cache_page_write_begin(struct address_space *mapping, + pgoff_t index, unsigned flags); /* * Returns locked page at given index in given cache, creating it if needed. diff --git a/mm/filemap.c b/mm/filemap.c index f3e5f8944d17..f8c69273c37f 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2140,19 +2140,24 @@ EXPORT_SYMBOL(generic_file_direct_write); * Find or create a page at the given pagecache position. Return the locked * page. This function is specifically for buffered writes. */ -struct page *__grab_cache_page(struct address_space *mapping, pgoff_t index) +struct page *grab_cache_page_write_begin(struct address_space *mapping, + pgoff_t index, unsigned flags) { int status; struct page *page; + gfp_t gfp_notmask = 0; + if (flags & AOP_FLAG_NOFS) + gfp_notmask = __GFP_FS; repeat: page = find_lock_page(mapping, index); if (likely(page)) return page; - page = page_cache_alloc(mapping); + page = __page_cache_alloc(mapping_gfp_mask(mapping) & ~gfp_notmask); if (!page) return NULL; - status = add_to_page_cache_lru(page, mapping, index, GFP_KERNEL); + status = add_to_page_cache_lru(page, mapping, index, + GFP_KERNEL & ~gfp_notmask); if (unlikely(status)) { page_cache_release(page); if (status == -EEXIST) @@ -2161,7 +2166,7 @@ repeat: } return page; } -EXPORT_SYMBOL(__grab_cache_page); +EXPORT_SYMBOL(grab_cache_page_write_begin); static ssize_t generic_perform_write(struct file *file, struct iov_iter *i, loff_t pos) -- cgit v1.2.3 From 099e657625e801adf82054c8050dde5aceb68452 Mon Sep 17 00:00:00 2001 From: Alessandro Zummo Date: Sun, 4 Jan 2009 12:00:54 -0800 Subject: rtc: add alarm/update irq interfaces Add standard interfaces for alarm/update irqs enabling. Drivers are no more required to implement equivalent ioctl code as rtc-dev will provide it. UIE emulation should now be handled correctly and will work even for those RTC drivers who cannot be configured to do both UIE and AIE. Signed-off-by: Alessandro Zummo Cc: David Brownell Cc: Atsushi Nemoto Cc: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 6 +++++- drivers/rtc/interface.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/rtc/rtc-dev.c | 51 +++++++++++++++++++++++++++++++--------------- include/linux/rtc.h | 8 +++++++- 4 files changed, 101 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 123092d8a984..165a81843357 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -102,9 +102,13 @@ config RTC_INTF_DEV_UIE_EMUL depends on RTC_INTF_DEV help Provides an emulation for RTC_UIE if the underlying rtc chip - driver does not expose RTC_UIE ioctls. Those requests generate + driver does not expose RTC_UIE ioctls. Those requests generate once-per-second update interrupts, used for synchronization. + The emulation code will read the time from the hardware + clock several times per second, please enable this option + only if you know that you really need it. + config RTC_DRV_TEST tristate "Test driver/device" help diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index a04c1b6b1575..fd2c652504ff 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -307,6 +307,60 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } EXPORT_SYMBOL_GPL(rtc_set_alarm); +int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) +{ + int err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + if (!rtc->ops) + err = -ENODEV; + else if (!rtc->ops->alarm_irq_enable) + err = -EINVAL; + else + err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled); + + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable); + +int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) +{ + int err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + if (enabled == 0 && rtc->uie_irq_active) { + mutex_unlock(&rtc->ops_lock); + return rtc_dev_update_irq_enable_emul(rtc, enabled); + } +#endif + + if (!rtc->ops) + err = -ENODEV; + else if (!rtc->ops->update_irq_enable) + err = -EINVAL; + else + err = rtc->ops->update_irq_enable(rtc->dev.parent, enabled); + + mutex_unlock(&rtc->ops_lock); + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + /* + * Enable emulation if the driver did not provide + * the update_irq_enable function pointer or if returned + * -EINVAL to signal that it has been configured without + * interrupts or that are not available at the moment. + */ + if (err == -EINVAL) + err = rtc_dev_update_irq_enable_emul(rtc, enabled); +#endif + return err; +} +EXPORT_SYMBOL_GPL(rtc_update_irq_enable); + /** * rtc_update_irq - report RTC periodic, alarm, and/or update irqs * @rtc: the rtc device diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index ecdea44ae4e5..45152f4952d6 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -92,10 +92,10 @@ static void rtc_uie_timer(unsigned long data) spin_unlock_irqrestore(&rtc->irq_lock, flags); } -static void clear_uie(struct rtc_device *rtc) +static int clear_uie(struct rtc_device *rtc) { spin_lock_irq(&rtc->irq_lock); - if (rtc->irq_active) { + if (rtc->uie_irq_active) { rtc->stop_uie_polling = 1; if (rtc->uie_timer_active) { spin_unlock_irq(&rtc->irq_lock); @@ -108,9 +108,10 @@ static void clear_uie(struct rtc_device *rtc) flush_scheduled_work(); spin_lock_irq(&rtc->irq_lock); } - rtc->irq_active = 0; + rtc->uie_irq_active = 0; } spin_unlock_irq(&rtc->irq_lock); + return 0; } static int set_uie(struct rtc_device *rtc) @@ -122,8 +123,8 @@ static int set_uie(struct rtc_device *rtc) if (err) return err; spin_lock_irq(&rtc->irq_lock); - if (!rtc->irq_active) { - rtc->irq_active = 1; + if (!rtc->uie_irq_active) { + rtc->uie_irq_active = 1; rtc->stop_uie_polling = 0; rtc->oldsecs = tm.tm_sec; rtc->uie_task_active = 1; @@ -134,6 +135,16 @@ static int set_uie(struct rtc_device *rtc) spin_unlock_irq(&rtc->irq_lock); return 0; } + +int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, unsigned int enabled) +{ + if (enabled) + return set_uie(rtc); + else + return clear_uie(rtc); +} +EXPORT_SYMBOL(rtc_dev_update_irq_enable_emul); + #endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */ static ssize_t @@ -357,6 +368,22 @@ static long rtc_dev_ioctl(struct file *file, err = rtc_irq_set_state(rtc, NULL, 0); break; + case RTC_AIE_ON: + mutex_unlock(&rtc->ops_lock); + return rtc_alarm_irq_enable(rtc, 1); + + case RTC_AIE_OFF: + mutex_unlock(&rtc->ops_lock); + return rtc_alarm_irq_enable(rtc, 0); + + case RTC_UIE_ON: + mutex_unlock(&rtc->ops_lock); + return rtc_update_irq_enable(rtc, 1); + + case RTC_UIE_OFF: + mutex_unlock(&rtc->ops_lock); + return rtc_update_irq_enable(rtc, 0); + case RTC_IRQP_SET: err = rtc_irq_set_freq(rtc, NULL, arg); break; @@ -401,17 +428,6 @@ static long rtc_dev_ioctl(struct file *file, err = -EFAULT; return err; -#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL - case RTC_UIE_OFF: - mutex_unlock(&rtc->ops_lock); - clear_uie(rtc); - return 0; - - case RTC_UIE_ON: - mutex_unlock(&rtc->ops_lock); - err = set_uie(rtc); - return err; -#endif default: err = -ENOTTY; break; @@ -440,7 +456,10 @@ static int rtc_dev_release(struct inode *inode, struct file *file) * Leave the alarm alone; it may be set to trigger a system wakeup * later, or be used by kernel code, and is a one-shot event anyway. */ + + /* Keep ioctl until all drivers are converted */ rtc_dev_ioctl(file, RTC_UIE_OFF, 0); + rtc_update_irq_enable(rtc, 0); rtc_irq_set_state(rtc, NULL, 0); if (rtc->ops->release) diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 91f597ad6acc..4046b75563c1 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -145,6 +145,8 @@ struct rtc_class_ops { int (*irq_set_state)(struct device *, int enabled); int (*irq_set_freq)(struct device *, int freq); int (*read_callback)(struct device *, int data); + int (*alarm_irq_enable)(struct device *, unsigned int enabled); + int (*update_irq_enable)(struct device *, unsigned int enabled); }; #define RTC_DEVICE_NAME_SIZE 20 @@ -181,7 +183,7 @@ struct rtc_device struct timer_list uie_timer; /* Those fields are protected by rtc->irq_lock */ unsigned int oldsecs; - unsigned int irq_active:1; + unsigned int uie_irq_active:1; unsigned int stop_uie_polling:1; unsigned int uie_task_active:1; unsigned int uie_timer_active:1; @@ -216,6 +218,10 @@ extern int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled); extern int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq); +extern int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled); +extern int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled); +extern int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, + unsigned int enabled); typedef struct rtc_task { void (*func)(void *private_data); -- cgit v1.2.3